everything: Rename branches to bookmarks

Jujutsu's branches do not behave like Git branches, which is a major
hurdle for people adopting it from Git. They rather behave like
Mercurial's (hg) bookmarks. 

We've had multiple discussions about it in the last ~1.5 years about this rename in the Discord, 
where multiple people agreed that this _false_ familiarity does not help anyone. Initially we were 
reluctant to do it but overtime, more and more users agreed that `bookmark` was a better for name 
the current mechanism. This may be hard break for current `jj branch` users, but it will immensly 
help Jujutsu's future, by defining it as our first own term. The `[experimental-moving-branches]` 
config option is currently left alone, to force not another large config update for
users, since the last time this happened was when `jj log -T show` was removed, which immediately 
resulted in breaking users and introduced soft deprecations.

This name change will also make it easier to introduce Topics (#3402) as _topological branches_ 
with a easier model. 

This was mostly done via LSP, ripgrep and sed and a whole bunch of manual changes either from
me being lazy or thankfully pointed out by reviewers.
This commit is contained in:
Philip Metzger 2024-08-21 21:59:15 +02:00 committed by Philip Metzger
parent 831404628f
commit d9c68e08b1
96 changed files with 4574 additions and 4273 deletions

View file

@ -13,11 +13,22 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
* Invalid `ui.graph.style` configuration is now an error.
* The builtin template `branch_list` has been renamed to `bookmark_list` in
lieu of the `jj branch` deprecation.
### Deprecations
* `jj obslog` is now called `jj evolution-log`/`jj evolog`. `jj obslog` remains
as an alias.
* `jj branch` has been deprecated in favor of `jj bookmark`.
**Rationale:** Jujutsu's branches don't behave like Git branches, which a
confused many newcomers, as they expected a similar behavior given the name.
We've renamed them to "bookmarks" to match the actual behavior, as we think
that describes them better, and they also behave similar to Mercurial's
bookmarks.
### New features
* The new config option `snapshot.auto-track` lets you automatically track only

View file

@ -512,33 +512,33 @@ impl ReadonlyUserRepo {
}
}
/// A branch that should be advanced to satisfy the "advance-branches" feature.
/// This is a helper for `WorkspaceCommandTransaction`. It provides a type-safe
/// way to separate the work of checking whether a branch can be advanced and
/// actually advancing it. Advancing the branch never fails, but can't be done
/// until the new `CommitId` is available. Splitting the work in this way also
/// allows us to identify eligible branches without actually moving them and
/// return config errors to the user early.
pub struct AdvanceableBranch {
/// A bookmark that should be advanced to satisfy the "advance-bookmarks"
/// feature. This is a helper for `WorkspaceCommandTransaction`. It provides a
/// type-safe way to separate the work of checking whether a bookmark can be
/// advanced and actually advancing it. Advancing the bookmark never fails, but
/// can't be done until the new `CommitId` is available. Splitting the work in
/// this way also allows us to identify eligible bookmarks without actually
/// moving them and return config errors to the user early.
pub struct AdvanceableBookmark {
name: String,
old_commit_id: CommitId,
}
/// Helper for parsing and evaluating settings for the advance-branches feature.
/// Settings are configured in the jj config.toml as lists of [`StringPattern`]s
/// for enabled and disabled branches. Example:
/// Helper for parsing and evaluating settings for the advance-bookmarks
/// feature. Settings are configured in the jj config.toml as lists of
/// [`StringPattern`]s for enabled and disabled bookmarks. Example:
/// ```toml
/// [experimental-advance-branches]
/// # Enable the feature for all branches except "main".
/// enabled-branches = ["glob:*"]
/// disabled-branches = ["main"]
/// ```
struct AdvanceBranchesSettings {
enabled_branches: Vec<StringPattern>,
disabled_branches: Vec<StringPattern>,
struct AdvanceBookmarksSettings {
enabled_bookmarks: Vec<StringPattern>,
disabled_bookmarks: Vec<StringPattern>,
}
impl AdvanceBranchesSettings {
impl AdvanceBookmarksSettings {
fn from_config(config: &config::Config) -> Result<Self, CommandError> {
let get_setting = |setting_key| {
let setting = format!("experimental-advance-branches.{setting_key}");
@ -558,28 +558,30 @@ impl AdvanceBranchesSettings {
}
};
Ok(Self {
enabled_branches: get_setting("enabled-branches")?,
disabled_branches: get_setting("disabled-branches")?,
enabled_bookmarks: get_setting("enabled-branches")?,
disabled_bookmarks: get_setting("disabled-branches")?,
})
}
/// Returns true if the advance-branches feature is enabled for
/// `branch_name`.
fn branch_is_eligible(&self, branch_name: &str) -> bool {
/// Returns true if the advance-bookmarks feature is enabled for
/// `bookmark_name`.
fn bookmark_is_eligible(&self, bookmark_name: &str) -> bool {
if self
.disabled_branches
.disabled_bookmarks
.iter()
.any(|d| d.matches(branch_name))
.any(|d| d.matches(bookmark_name))
{
return false;
}
self.enabled_branches.iter().any(|e| e.matches(branch_name))
self.enabled_bookmarks
.iter()
.any(|e| e.matches(bookmark_name))
}
/// Returns true if the config includes at least one "enabled-branches"
/// pattern.
fn feature_enabled(&self) -> bool {
!self.enabled_branches.is_empty()
!self.enabled_bookmarks.is_empty()
}
}
@ -855,10 +857,10 @@ impl WorkspaceCommandHelper {
}
/// Imports branches and tags from the underlying Git repo, abandons old
/// branches.
/// bookmarks.
///
/// If the working-copy branch is rebased, and if update is allowed, the new
/// working-copy commit will be checked out.
/// If the working-copy branch is rebased, and if update is allowed, the
/// new working-copy commit will be checked out.
///
/// This function does not import the Git HEAD, but the HEAD may be reset to
/// the working copy parent if the repository is colocated.
@ -1479,8 +1481,8 @@ See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy \
}
if self.working_copy_shared_with_git {
let failed_branches = git::export_refs(mut_repo)?;
print_failed_git_export(ui, &failed_branches)?;
let refs = git::export_refs(mut_repo)?;
print_failed_git_export(ui, &refs)?;
}
self.user_repo = ReadonlyUserRepo::new(tx.commit("snapshot working copy"));
@ -1596,8 +1598,8 @@ See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy \
if let Some(wc_commit) = &maybe_new_wc_commit {
git::reset_head(tx.repo_mut(), &git_repo, wc_commit)?;
}
let failed_branches = git::export_refs(tx.repo_mut())?;
print_failed_git_export(ui, &failed_branches)?;
let refs = git::export_refs(tx.repo_mut())?;
print_failed_git_export(ui, &refs)?;
}
self.user_repo = ReadonlyUserRepo::new(tx.commit(description));
@ -1781,34 +1783,35 @@ Then run `jj squash` to move the resolution into the conflicted commit."#,
Ok(())
}
/// Identifies branches which are eligible to be moved automatically during
/// `jj commit` and `jj new`. Whether a branch is eligible is determined by
/// its target and the user and repo config for "advance-branches".
/// Identifies bookmarks which are eligible to be moved automatically
/// during `jj commit` and `jj new`. Whether a bookmark is eligible is
/// determined by its target and the user and repo config for
/// "advance-bookmarks".
///
/// Returns a Vec of branches in `repo` that point to any of the `from`
/// Returns a Vec of bookmarks in `repo` that point to any of the `from`
/// commits and that are eligible to advance. The `from` commits are
/// typically the parents of the target commit of `jj commit` or `jj new`.
///
/// Branches are not moved until
/// `WorkspaceCommandTransaction::advance_branches()` is called with the
/// `AdvanceableBranch`s returned by this function.
/// `WorkspaceCommandTransaction::advance_bookmarks()` is called with the
/// `AdvanceableBookmark`s returned by this function.
///
/// Returns an empty `std::Vec` if no branches are eligible to advance.
pub fn get_advanceable_branches<'a>(
/// Returns an empty `std::Vec` if no bookmarks are eligible to advance.
pub fn get_advanceable_bookmarks<'a>(
&self,
from: impl IntoIterator<Item = &'a CommitId>,
) -> Result<Vec<AdvanceableBranch>, CommandError> {
let ab_settings = AdvanceBranchesSettings::from_config(self.settings().config())?;
) -> Result<Vec<AdvanceableBookmark>, CommandError> {
let ab_settings = AdvanceBookmarksSettings::from_config(self.settings().config())?;
if !ab_settings.feature_enabled() {
// Return early if we know that there's no work to do.
return Ok(Vec::new());
}
let mut advanceable_branches = Vec::new();
let mut advanceable_bookmarks = Vec::new();
for from_commit in from {
for (name, _) in self.repo().view().local_branches_for_commit(from_commit) {
if ab_settings.branch_is_eligible(name) {
advanceable_branches.push(AdvanceableBranch {
for (name, _) in self.repo().view().local_bookmarks_for_commit(from_commit) {
if ab_settings.bookmark_is_eligible(name) {
advanceable_bookmarks.push(AdvanceableBookmark {
name: name.to_owned(),
old_commit_id: from_commit.clone(),
});
@ -1816,7 +1819,7 @@ Then run `jj squash` to move the resolution into the conflicted commit."#,
}
}
Ok(advanceable_branches)
Ok(advanceable_bookmarks)
}
}
@ -1928,18 +1931,18 @@ impl WorkspaceCommandTransaction<'_> {
self.tx
}
/// Moves each branch in `branches` from an old commit it's associated with
/// (configured by `get_advanceable_branches`) to the `move_to` commit. If
/// the branch is conflicted before the update, it will remain conflicted
/// after the update, but the conflict will involve the `move_to` commit
/// instead of the old commit.
pub fn advance_branches(&mut self, branches: Vec<AdvanceableBranch>, move_to: &CommitId) {
for branch in branches {
// This removes the old commit ID from the branch's RefTarget and
/// Moves each bookmark in `bookmarks` from an old commit it's associated
/// with (configured by `get_advanceable_bookmarks`) to the `move_to`
/// commit. If the bookmark is conflicted before the update, it will
/// remain conflicted after the update, but the conflict will involve
/// the `move_to` commit instead of the old commit.
pub fn advance_bookmarks(&mut self, bookmarks: Vec<AdvanceableBookmark>, move_to: &CommitId) {
for bookmark in bookmarks {
// This removes the old commit ID from the bookmark's RefTarget and
// replaces it with the `move_to` ID.
self.repo_mut().merge_local_branch(
&branch.name,
&RefTarget::normal(branch.old_commit_id),
self.repo_mut().merge_local_bookmark(
&bookmark.name,
&RefTarget::normal(bookmark.old_commit_id),
&RefTarget::normal(move_to.clone()),
);
}
@ -2233,35 +2236,35 @@ pub fn print_unmatched_explicit_paths<'a>(
Ok(())
}
pub fn print_trackable_remote_branches(ui: &Ui, view: &View) -> io::Result<()> {
let remote_branch_names = view
.branches()
.filter(|(_, branch_target)| branch_target.local_target.is_present())
.flat_map(|(name, branch_target)| {
branch_target
pub fn print_trackable_remote_bookmarks(ui: &Ui, view: &View) -> io::Result<()> {
let remote_bookmark_names = view
.bookmarks()
.filter(|(_, bookmark_target)| bookmark_target.local_target.is_present())
.flat_map(|(name, bookmark_target)| {
bookmark_target
.remote_refs
.into_iter()
.filter(|&(_, remote_ref)| !remote_ref.is_tracking())
.map(move |(remote, _)| format!("{name}@{remote}"))
})
.collect_vec();
if remote_branch_names.is_empty() {
if remote_bookmark_names.is_empty() {
return Ok(());
}
if let Some(mut formatter) = ui.status_formatter() {
writeln!(
formatter.labeled("hint").with_heading("Hint: "),
"The following remote branches aren't associated with the existing local branches:"
"The following remote bookmarks aren't associated with the existing local bookmarks:"
)?;
for full_name in &remote_branch_names {
for full_name in &remote_bookmark_names {
write!(formatter, " ")?;
writeln!(formatter.labeled("branch"), "{full_name}")?;
writeln!(formatter.labeled("bookmark"), "{full_name}")?;
}
writeln!(
formatter.labeled("hint").with_heading("Hint: "),
"Run `jj branch track {names}` to keep local branches updated on future pulls.",
names = remote_branch_names.join(" "),
"Run `jj bookmark track {names}` to keep local bookmarks updated on future pulls.",
names = remote_bookmark_names.join(" "),
)?;
}
Ok(())
@ -2509,30 +2512,30 @@ impl DiffSelector {
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RemoteBranchName {
pub branch: String,
pub struct RemoteBookmarkName {
pub bookmark: String,
pub remote: String,
}
impl fmt::Display for RemoteBranchName {
impl fmt::Display for RemoteBookmarkName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let RemoteBranchName { branch, remote } = self;
write!(f, "{branch}@{remote}")
let RemoteBookmarkName { bookmark, remote } = self;
write!(f, "{bookmark}@{remote}")
}
}
#[derive(Clone, Debug)]
pub struct RemoteBranchNamePattern {
pub branch: StringPattern,
pub struct RemoteBookmarkNamePattern {
pub bookmark: StringPattern,
pub remote: StringPattern,
}
impl FromStr for RemoteBranchNamePattern {
impl FromStr for RemoteBookmarkNamePattern {
type Err = String;
fn from_str(src: &str) -> Result<Self, Self::Err> {
// The kind prefix applies to both branch and remote fragments. It's
// weird that unanchored patterns like substring:branch@remote is split
// The kind prefix applies to both bookmark and remote fragments. It's
// weird that unanchored patterns like substring:bookmark@remote is split
// into two, but I can't think of a better syntax.
// TODO: should we disable substring pattern? what if we added regex?
let (maybe_kind, pat) = src
@ -2545,27 +2548,27 @@ impl FromStr for RemoteBranchNamePattern {
Ok(StringPattern::exact(pat))
}
};
// TODO: maybe reuse revset parser to handle branch/remote name containing @
let (branch, remote) = pat
.rsplit_once('@')
.ok_or_else(|| "remote branch must be specified in branch@remote form".to_owned())?;
Ok(RemoteBranchNamePattern {
branch: to_pattern(branch)?,
// TODO: maybe reuse revset parser to handle bookmark/remote name containing @
let (bookmark, remote) = pat.rsplit_once('@').ok_or_else(|| {
"remote bookmark must be specified in bookmark@remote form".to_owned()
})?;
Ok(RemoteBookmarkNamePattern {
bookmark: to_pattern(bookmark)?,
remote: to_pattern(remote)?,
})
}
}
impl RemoteBranchNamePattern {
impl RemoteBookmarkNamePattern {
pub fn is_exact(&self) -> bool {
self.branch.is_exact() && self.remote.is_exact()
self.bookmark.is_exact() && self.remote.is_exact()
}
}
impl fmt::Display for RemoteBranchNamePattern {
impl fmt::Display for RemoteBookmarkNamePattern {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let RemoteBranchNamePattern { branch, remote } = self;
write!(f, "{branch}@{remote}")
let RemoteBookmarkNamePattern { bookmark, remote } = self;
write!(f, "{bookmark}@{remote}")
}
}

View file

@ -16,80 +16,82 @@ use clap::builder::NonEmptyStringValueParser;
use jj_lib::object_id::ObjectId as _;
use jj_lib::op_store::RefTarget;
use super::has_tracked_remote_branches;
use super::has_tracked_remote_bookmarks;
use crate::cli_util::CommandHelper;
use crate::cli_util::RevisionArg;
use crate::command_error::user_error_with_hint;
use crate::command_error::CommandError;
use crate::ui::Ui;
/// Create a new branch
/// Create a new bookmark
#[derive(clap::Args, Clone, Debug)]
pub struct BranchCreateArgs {
/// The branch's target revision
pub struct BookmarkCreateArgs {
/// The bookmark's target revision
//
// The `--to` alias exists for making it easier for the user to switch
// between `branch create`, `branch move`, and `branch set`.
// between `bookmark create`, `bookmark move`, and `bookmark set`.
#[arg(long, short, visible_alias = "to")]
revision: Option<RevisionArg>,
/// The branches to create
/// The bookmarks to create
#[arg(required = true, value_parser = NonEmptyStringValueParser::new())]
names: Vec<String>,
}
pub fn cmd_branch_create(
pub fn cmd_bookmark_create(
ui: &mut Ui,
command: &CommandHelper,
args: &BranchCreateArgs,
args: &BookmarkCreateArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let target_commit =
workspace_command.resolve_single_rev(args.revision.as_ref().unwrap_or(&RevisionArg::AT))?;
let view = workspace_command.repo().view();
let branch_names = &args.names;
for name in branch_names {
if view.get_local_branch(name).is_present() {
let bookmark_names = &args.names;
for name in bookmark_names {
if view.get_local_bookmark(name).is_present() {
return Err(user_error_with_hint(
format!("Branch already exists: {name}"),
"Use `jj branch set` to update it.",
format!("Bookmark already exists: {name}"),
"Use `jj bookmark set` to update it.",
));
}
if has_tracked_remote_branches(view, name) {
if has_tracked_remote_bookmarks(view, name) {
return Err(user_error_with_hint(
format!("Tracked remote branches exist for deleted branch: {name}"),
format!("Tracked remote bookmarks exist for deleted bookmark: {name}"),
format!(
"Use `jj branch set` to recreate the local branch. Run `jj branch untrack \
'glob:{name}@*'` to disassociate them."
"Use `jj bookmark set` to recreate the local bookmark. Run `jj bookmark \
untrack 'glob:{name}@*'` to disassociate them."
),
));
}
}
let mut tx = workspace_command.start_transaction();
for branch_name in branch_names {
tx.repo_mut()
.set_local_branch_target(branch_name, RefTarget::normal(target_commit.id().clone()));
for bookmark_name in bookmark_names {
tx.repo_mut().set_local_bookmark_target(
bookmark_name,
RefTarget::normal(target_commit.id().clone()),
);
}
if let Some(mut formatter) = ui.status_formatter() {
write!(
formatter,
"Created {} branches pointing to ",
branch_names.len()
"Created {} bookmarks pointing to ",
bookmark_names.len()
)?;
tx.write_commit_summary(formatter.as_mut(), &target_commit)?;
writeln!(formatter)?;
}
if branch_names.len() > 1 && args.revision.is_none() {
if bookmark_names.len() > 1 && args.revision.is_none() {
writeln!(ui.hint_default(), "Use -r to specify the target revision.")?;
}
tx.finish(
ui,
format!(
"create branch {names} pointing to commit {id}",
names = branch_names.join(", "),
"create bookmark {names} pointing to commit {id}",
names = bookmark_names.join(", "),
id = target_commit.id().hex()
),
)?;

View file

@ -16,43 +16,47 @@ use itertools::Itertools as _;
use jj_lib::op_store::RefTarget;
use jj_lib::str_util::StringPattern;
use super::find_local_branches;
use super::find_local_bookmarks;
use crate::cli_util::CommandHelper;
use crate::command_error::CommandError;
use crate::ui::Ui;
/// Delete an existing branch and propagate the deletion to remotes on the
/// Delete an existing bookmark and propagate the deletion to remotes on the
/// next push
#[derive(clap::Args, Clone, Debug)]
pub struct BranchDeleteArgs {
/// The branches to delete
pub struct BookmarkDeleteArgs {
/// The bookmarks to delete
///
/// By default, the specified name matches exactly. Use `glob:` prefix to
/// select branches by wildcard pattern. For details, see
/// https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
/// select bookmarks by wildcard pattern. For details, see
/// https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
#[arg(required = true, value_parser = StringPattern::parse)]
names: Vec<StringPattern>,
}
pub fn cmd_branch_delete(
pub fn cmd_bookmark_delete(
ui: &mut Ui,
command: &CommandHelper,
args: &BranchDeleteArgs,
args: &BookmarkDeleteArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let repo = workspace_command.repo().clone();
let matched_branches = find_local_branches(repo.view(), &args.names)?;
let matched_bookmarks = find_local_bookmarks(repo.view(), &args.names)?;
let mut tx = workspace_command.start_transaction();
for (name, _) in &matched_branches {
for (name, _) in &matched_bookmarks {
tx.repo_mut()
.set_local_branch_target(name, RefTarget::absent());
.set_local_bookmark_target(name, RefTarget::absent());
}
writeln!(ui.status(), "Deleted {} branches.", matched_branches.len())?;
writeln!(
ui.status(),
"Deleted {} bookmarks.",
matched_bookmarks.len()
)?;
tx.finish(
ui,
format!(
"delete branch {}",
matched_branches.iter().map(|(name, _)| name).join(", ")
"delete bookmark {}",
matched_bookmarks.iter().map(|(name, _)| name).join(", ")
),
)?;
Ok(())

View file

@ -19,60 +19,60 @@ use jj_lib::op_store::RemoteRef;
use jj_lib::str_util::StringPattern;
use jj_lib::view::View;
use super::find_branches_with;
use super::find_bookmarks_with;
use crate::cli_util::CommandHelper;
use crate::command_error::CommandError;
use crate::ui::Ui;
/// Forget everything about a branch, including its local and remote
/// Forget everything about a bookmark, including its local and remote
/// targets
///
/// A forgotten branch will not impact remotes on future pushes. It will be
/// A forgotten bookmark will not impact remotes on future pushes. It will be
/// recreated on future pulls if it still exists in the remote.
#[derive(clap::Args, Clone, Debug)]
pub struct BranchForgetArgs {
/// The branches to forget
pub struct BookmarkForgetArgs {
/// The bookmarks to forget
///
/// By default, the specified name matches exactly. Use `glob:` prefix to
/// select branches by wildcard pattern. For details, see
/// https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
/// select bookmarks by wildcard pattern. For details, see
/// https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
#[arg(required = true, value_parser = StringPattern::parse)]
names: Vec<StringPattern>,
}
pub fn cmd_branch_forget(
pub fn cmd_bookmark_forget(
ui: &mut Ui,
command: &CommandHelper,
args: &BranchForgetArgs,
args: &BookmarkForgetArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let repo = workspace_command.repo().clone();
let matched_branches = find_forgettable_branches(repo.view(), &args.names)?;
let matched_bookmarks = find_forgettable_bookmarks(repo.view(), &args.names)?;
let mut tx = workspace_command.start_transaction();
for (name, branch_target) in &matched_branches {
for (name, bookmark_target) in &matched_bookmarks {
tx.repo_mut()
.set_local_branch_target(name, RefTarget::absent());
for (remote_name, _) in &branch_target.remote_refs {
.set_local_bookmark_target(name, RefTarget::absent());
for (remote_name, _) in &bookmark_target.remote_refs {
tx.repo_mut()
.set_remote_branch(name, remote_name, RemoteRef::absent());
.set_remote_bookmark(name, remote_name, RemoteRef::absent());
}
}
writeln!(ui.status(), "Forgot {} branches.", matched_branches.len())?;
writeln!(ui.status(), "Forgot {} bookmarks.", matched_bookmarks.len())?;
tx.finish(
ui,
format!(
"forget branch {}",
matched_branches.iter().map(|(name, _)| name).join(", ")
"forget bookmark {}",
matched_bookmarks.iter().map(|(name, _)| name).join(", ")
),
)?;
Ok(())
}
fn find_forgettable_branches<'a>(
fn find_forgettable_bookmarks<'a>(
view: &'a View,
name_patterns: &[StringPattern],
) -> Result<Vec<(&'a str, BranchTarget<'a>)>, CommandError> {
find_branches_with(name_patterns, |pattern| {
view.branches().filter(|(name, _)| pattern.matches(name))
find_bookmarks_with(name_patterns, |pattern| {
view.bookmarks().filter(|(name, _)| pattern.matches(name))
})
}

View file

@ -25,71 +25,71 @@ use crate::commit_templater::CommitTemplateLanguage;
use crate::commit_templater::RefName;
use crate::ui::Ui;
/// List branches and their targets
/// List bookmarks and their targets
///
/// By default, a tracking remote branch will be included only if its target is
/// different from the local target. A non-tracking remote branch won't be
/// listed. For a conflicted branch (both local and remote), old target
/// By default, a tracking remote bookmark will be included only if its target
/// is different from the local target. A non-tracking remote bookmark won't be
/// listed. For a conflicted bookmark (both local and remote), old target
/// revisions are preceded by a "-" and new target revisions are preceded by a
/// "+".
///
/// For information about branches, see
/// https://martinvonz.github.io/jj/latest/branches/.
/// For information about bookmarks, see
/// https://martinvonz.github.io/jj/docs/bookmarks.md.
#[derive(clap::Args, Clone, Debug)]
pub struct BranchListArgs {
/// Show all tracking and non-tracking remote branches including the ones
/// whose targets are synchronized with the local branches
pub struct BookmarkListArgs {
/// Show all tracking and non-tracking remote bookmarks including the ones
/// whose targets are synchronized with the local bookmarks
#[arg(long, short, alias = "all")]
all_remotes: bool,
/// Show remote tracked branches only. Omits local Git-tracking branches by
/// default
/// Show remote tracked bookmarks only. Omits local Git-tracking bookmarks
/// by default
#[arg(long, short, conflicts_with_all = ["all_remotes"])]
tracked: bool,
/// Show conflicted branches only
/// Show conflicted bookmarks only
#[arg(long, short, conflicts_with_all = ["all_remotes"])]
conflicted: bool,
/// Show branches whose local name matches
/// Show bookmarks whose local name matches
///
/// By default, the specified name matches exactly. Use `glob:` prefix to
/// select branches by wildcard pattern. For details, see
/// https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
/// select bookmarks by wildcard pattern. For details, see
/// https://martinvonz.github.io/jj/docs/revsets.md#string-patterns.
#[arg(value_parser = StringPattern::parse)]
names: Vec<StringPattern>,
/// Show branches whose local targets are in the given revisions
/// Show bookmarks whose local targets are in the given revisions
///
/// Note that `-r deleted_branch` will not work since `deleted_branch`
/// Note that `-r deleted_bookmark` will not work since `deleted_bookmark`
/// wouldn't have a local target.
#[arg(long, short)]
revisions: Vec<RevisionArg>,
/// Render each branch using the given template
/// Render each bookmark using the given template
///
/// All 0-argument methods of the `RefName` type are available as keywords.
///
/// For the syntax, see https://martinvonz.github.io/jj/latest/templates/
/// For the syntax, see https://martinvonz.github.io/jj/latest/docs/templates.md
#[arg(long, short = 'T')]
template: Option<String>,
}
pub fn cmd_branch_list(
pub fn cmd_bookmark_list(
ui: &mut Ui,
command: &CommandHelper,
args: &BranchListArgs,
args: &BookmarkListArgs,
) -> Result<(), CommandError> {
let workspace_command = command.workspace_helper(ui)?;
let repo = workspace_command.repo();
let view = repo.view();
// Like cmd_git_push(), names and revisions are OR-ed.
let branch_names_to_list = if !args.names.is_empty() || !args.revisions.is_empty() {
let mut branch_names: HashSet<&str> = HashSet::new();
let bookmark_names_to_list = if !args.names.is_empty() || !args.revisions.is_empty() {
let mut bookmark_names: HashSet<&str> = HashSet::new();
if !args.names.is_empty() {
branch_names.extend(
view.branches()
bookmark_names.extend(
view.bookmarks()
.filter(|&(name, _)| args.names.iter().any(|pattern| pattern.matches(name)))
.map(|(name, _)| name),
);
@ -97,18 +97,19 @@ pub fn cmd_branch_list(
if !args.revisions.is_empty() {
// Match against local targets only, which is consistent with "jj git push".
let mut expression = workspace_command.parse_union_revsets(&args.revisions)?;
// Intersects with the set of local branch targets to minimize the lookup space.
expression.intersect_with(&RevsetExpression::branches(StringPattern::everything()));
// Intersects with the set of local bookmark targets to minimize the lookup
// space.
expression.intersect_with(&RevsetExpression::bookmarks(StringPattern::everything()));
let filtered_targets: HashSet<_> = expression.evaluate_to_commit_ids()?.collect();
branch_names.extend(
view.local_branches()
bookmark_names.extend(
view.local_bookmarks()
.filter(|(_, target)| {
target.added_ids().any(|id| filtered_targets.contains(id))
})
.map(|(name, _)| name),
);
}
Some(branch_names)
Some(bookmark_names)
} else {
None
};
@ -117,27 +118,27 @@ pub fn cmd_branch_list(
let language = workspace_command.commit_template_language();
let text = match &args.template {
Some(value) => value.to_owned(),
None => command.settings().config().get("templates.branch_list")?,
None => command.settings().config().get("templates.bookmark_list")?,
};
workspace_command
.parse_template(&language, &text, CommitTemplateLanguage::wrap_ref_name)?
.labeled("branch_list")
.labeled("bookmark_list")
};
ui.request_pager();
let mut formatter = ui.stdout_formatter();
let mut found_deleted_local_branch = false;
let mut found_deleted_tracking_local_branch = false;
let branches_to_list = view.branches().filter(|(name, target)| {
branch_names_to_list
let mut found_deleted_local_bookmark = false;
let mut found_deleted_tracking_local_bookmark = false;
let bookmarks_to_list = view.bookmarks().filter(|(name, target)| {
bookmark_names_to_list
.as_ref()
.map_or(true, |branch_names| branch_names.contains(name))
.map_or(true, |bookmark_names| bookmark_names.contains(name))
&& (!args.conflicted || target.local_target.has_conflict())
});
for (name, branch_target) in branches_to_list {
let local_target = branch_target.local_target;
let remote_refs = branch_target.remote_refs;
for (name, bookmark_target) in bookmarks_to_list {
let local_target = bookmark_target.local_target;
let remote_refs = bookmark_target.remote_refs;
let (mut tracking_remote_refs, untracked_remote_refs) = remote_refs
.iter()
.copied()
@ -165,8 +166,8 @@ pub fn cmd_branch_list(
}
if local_target.is_absent() && !tracking_remote_refs.is_empty() {
found_deleted_local_branch = true;
found_deleted_tracking_local_branch |= tracking_remote_refs
found_deleted_local_bookmark = true;
found_deleted_tracking_local_bookmark |= tracking_remote_refs
.iter()
.any(|&(remote, _)| remote != git::REMOTE_NAME_FOR_LOCAL_GIT_REPO);
}
@ -182,18 +183,18 @@ pub fn cmd_branch_list(
drop(formatter);
// Print only one of these hints. It's not important to mention unexported
// branches, but user might wonder why deleted branches are still listed.
if found_deleted_tracking_local_branch {
// bookmarks, but user might wonder why deleted bookmarks are still listed.
if found_deleted_tracking_local_bookmark {
writeln!(
ui.hint_default(),
"Branches marked as deleted will be *deleted permanently* on the remote on the next \
`jj git push`. Use `jj branch forget` to prevent this."
"Bookmarkes marked as deleted will be *deleted permanently* on the remote on the next \
`jj git push`. Use `jj bookmark forget` to prevent this."
)?;
} else if found_deleted_local_branch {
} else if found_deleted_local_bookmark {
writeln!(
ui.hint_default(),
"Branches marked as deleted will be deleted from the underlying Git repo on the next \
`jj git export`."
"Bookmarkes marked as deleted will be deleted from the underlying Git repo on the \
next `jj git export`."
)?;
}

View file

@ -0,0 +1,198 @@
// Copyright 2020-2023 The Jujutsu Authors
//
// 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.
mod create;
mod delete;
mod forget;
mod list;
mod r#move;
mod rename;
mod set;
mod track;
mod untrack;
use itertools::Itertools as _;
use jj_lib::backend::CommitId;
use jj_lib::git;
use jj_lib::op_store::RefTarget;
use jj_lib::op_store::RemoteRef;
use jj_lib::repo::Repo;
use jj_lib::str_util::StringPattern;
use jj_lib::view::View;
use self::create::cmd_bookmark_create;
use self::create::BookmarkCreateArgs;
use self::delete::cmd_bookmark_delete;
use self::delete::BookmarkDeleteArgs;
use self::forget::cmd_bookmark_forget;
use self::forget::BookmarkForgetArgs;
use self::list::cmd_bookmark_list;
use self::list::BookmarkListArgs;
use self::r#move::cmd_bookmark_move;
use self::r#move::BookmarkMoveArgs;
use self::rename::cmd_bookmark_rename;
use self::rename::BookmarkRenameArgs;
use self::set::cmd_bookmark_set;
use self::set::BookmarkSetArgs;
use self::track::cmd_bookmark_track;
use self::track::BookmarkTrackArgs;
use self::untrack::cmd_bookmark_untrack;
use self::untrack::BookmarkUntrackArgs;
use crate::cli_util::CommandHelper;
use crate::cli_util::RemoteBookmarkName;
use crate::cli_util::RemoteBookmarkNamePattern;
use crate::command_error::user_error;
use crate::command_error::CommandError;
use crate::ui::Ui;
/// Manage bookmarks
///
/// For information about bookmarks, see
/// https://martinvonz.github.io/jj/latest/docs/bookmarks.md.
#[derive(clap::Subcommand, Clone, Debug)]
pub enum BookmarkCommand {
#[command(visible_alias("c"))]
Create(BookmarkCreateArgs),
#[command(visible_alias("d"))]
Delete(BookmarkDeleteArgs),
#[command(visible_alias("f"))]
Forget(BookmarkForgetArgs),
#[command(visible_alias("l"))]
List(BookmarkListArgs),
#[command(visible_alias("m"))]
Move(BookmarkMoveArgs),
#[command(visible_alias("r"))]
Rename(BookmarkRenameArgs),
#[command(visible_alias("s"))]
Set(BookmarkSetArgs),
#[command(visible_alias("t"))]
Track(BookmarkTrackArgs),
Untrack(BookmarkUntrackArgs),
}
pub fn cmd_bookmark(
ui: &mut Ui,
command: &CommandHelper,
subcommand: &BookmarkCommand,
) -> Result<(), CommandError> {
match subcommand {
BookmarkCommand::Create(args) => cmd_bookmark_create(ui, command, args),
BookmarkCommand::Delete(args) => cmd_bookmark_delete(ui, command, args),
BookmarkCommand::Forget(args) => cmd_bookmark_forget(ui, command, args),
BookmarkCommand::List(args) => cmd_bookmark_list(ui, command, args),
BookmarkCommand::Move(args) => cmd_bookmark_move(ui, command, args),
BookmarkCommand::Rename(args) => cmd_bookmark_rename(ui, command, args),
BookmarkCommand::Set(args) => cmd_bookmark_set(ui, command, args),
BookmarkCommand::Track(args) => cmd_bookmark_track(ui, command, args),
BookmarkCommand::Untrack(args) => cmd_bookmark_untrack(ui, command, args),
}
}
fn find_local_bookmarks<'a>(
view: &'a View,
name_patterns: &[StringPattern],
) -> Result<Vec<(&'a str, &'a RefTarget)>, CommandError> {
find_bookmarks_with(name_patterns, |pattern| {
view.local_bookmarks_matching(pattern)
})
}
fn find_bookmarks_with<'a, 'b, V, I: Iterator<Item = (&'a str, V)>>(
name_patterns: &'b [StringPattern],
mut find_matches: impl FnMut(&'b StringPattern) -> I,
) -> Result<Vec<I::Item>, CommandError> {
let mut matching_bookmarks: Vec<I::Item> = vec![];
let mut unmatched_patterns = vec![];
for pattern in name_patterns {
let mut matches = find_matches(pattern).peekable();
if matches.peek().is_none() {
unmatched_patterns.push(pattern);
}
matching_bookmarks.extend(matches);
}
match &unmatched_patterns[..] {
[] => {
matching_bookmarks.sort_unstable_by_key(|(name, _)| *name);
matching_bookmarks.dedup_by_key(|(name, _)| *name);
Ok(matching_bookmarks)
}
[pattern] if pattern.is_exact() => Err(user_error(format!("No such bookmark: {pattern}"))),
patterns => Err(user_error(format!(
"No matching bookmarks for patterns: {}",
patterns.iter().join(", ")
))),
}
}
fn find_remote_bookmarks<'a>(
view: &'a View,
name_patterns: &[RemoteBookmarkNamePattern],
) -> Result<Vec<(RemoteBookmarkName, &'a RemoteRef)>, CommandError> {
let mut matching_bookmarks = vec![];
let mut unmatched_patterns = vec![];
for pattern in name_patterns {
let mut matches = view
.remote_bookmarks_matching(&pattern.bookmark, &pattern.remote)
.map(|((bookmark, remote), remote_ref)| {
let name = RemoteBookmarkName {
bookmark: bookmark.to_owned(),
remote: remote.to_owned(),
};
(name, remote_ref)
})
.peekable();
if matches.peek().is_none() {
unmatched_patterns.push(pattern);
}
matching_bookmarks.extend(matches);
}
match &unmatched_patterns[..] {
[] => {
matching_bookmarks.sort_unstable_by(|(name1, _), (name2, _)| name1.cmp(name2));
matching_bookmarks.dedup_by(|(name1, _), (name2, _)| name1 == name2);
Ok(matching_bookmarks)
}
[pattern] if pattern.is_exact() => {
Err(user_error(format!("No such remote bookmark: {pattern}")))
}
patterns => Err(user_error(format!(
"No matching remote bookmarks for patterns: {}",
patterns.iter().join(", ")
))),
}
}
/// Whether or not the `bookmark` has any tracked remotes (i.e. is a tracking
/// local bookmark.)
fn has_tracked_remote_bookmarks(view: &View, bookmark: &str) -> bool {
view.remote_bookmarks_matching(
&StringPattern::exact(bookmark),
&StringPattern::everything(),
)
.filter(|&((_, remote_name), _)| remote_name != git::REMOTE_NAME_FOR_LOCAL_GIT_REPO)
.any(|(_, remote_ref)| remote_ref.is_tracking())
}
fn is_fast_forward(repo: &dyn Repo, old_target: &RefTarget, new_target_id: &CommitId) -> bool {
if old_target.is_present() {
// Strictly speaking, "all" old targets should be ancestors, but we allow
// conflict resolution by setting bookmark to "any" of the old target
// descendants.
old_target
.added_ids()
.any(|old| repo.index().is_ancestor(old, new_target_id))
} else {
true
}
}

View file

@ -18,7 +18,7 @@ use jj_lib::object_id::ObjectId as _;
use jj_lib::op_store::RefTarget;
use jj_lib::str_util::StringPattern;
use super::find_branches_with;
use super::find_bookmarks_with;
use super::is_fast_forward;
use crate::cli_util::CommandHelper;
use crate::cli_util::RevisionArg;
@ -26,51 +26,52 @@ use crate::command_error::user_error_with_hint;
use crate::command_error::CommandError;
use crate::ui::Ui;
/// Move existing branches to target revision
/// Move existing bookmarks to target revision
///
/// If branch names are given, the specified branches will be updated to point
/// to the target revision.
/// If bookmark names are given, the specified bookmarks will be updated to
/// point to the target revision.
///
/// If `--from` options are given, branches currently pointing to the specified
/// revisions will be updated. The branches can also be filtered by names.
/// If `--from` options are given, bookmarks currently pointing to the
/// specified revisions will be updated. The bookmarks can also be filtered by
/// names.
///
/// Example: pull up the nearest branches to the working-copy parent
/// Example: pull up the nearest bookmarks to the working-copy parent
///
/// $ jj branch move --from 'heads(::@- & branches())' --to @-
/// $ jj bookmark move --from 'heads(::@- & bookmarks())' --to @-
#[derive(clap::Args, Clone, Debug)]
#[command(group(clap::ArgGroup::new("source").multiple(true).required(true)))]
pub struct BranchMoveArgs {
/// Move branches from the given revisions
pub struct BookmarkMoveArgs {
/// Move bookmarks from the given revisions
#[arg(long, group = "source", value_name = "REVISIONS")]
from: Vec<RevisionArg>,
/// Move branches to this revision
/// Move bookmarks to this revision
#[arg(long, default_value = "@", value_name = "REVISION")]
to: RevisionArg,
/// Allow moving branches backwards or sideways
/// Allow moving bookmarks backwards or sideways
#[arg(long, short = 'B')]
allow_backwards: bool,
/// Move branches matching the given name patterns
/// Move bookmarks matching the given name patterns
///
/// By default, the specified name matches exactly. Use `glob:` prefix to
/// select branches by wildcard pattern. For details, see
/// select bookmarks by wildcard pattern. For details, see
/// https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
#[arg(group = "source", value_parser = StringPattern::parse)]
names: Vec<StringPattern>,
}
pub fn cmd_branch_move(
pub fn cmd_bookmark_move(
ui: &mut Ui,
command: &CommandHelper,
args: &BranchMoveArgs,
args: &BookmarkMoveArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let repo = workspace_command.repo().clone();
let target_commit = workspace_command.resolve_single_rev(&args.to)?;
let matched_branches = {
let matched_bookmarks = {
let is_source_commit = if !args.from.is_empty() {
workspace_command
.parse_union_revsets(&args.from)?
@ -79,63 +80,63 @@ pub fn cmd_branch_move(
} else {
Box::new(|_: &CommitId| true)
};
let mut branches = if !args.names.is_empty() {
find_branches_with(&args.names, |pattern| {
let mut bookmarks = if !args.names.is_empty() {
find_bookmarks_with(&args.names, |pattern| {
repo.view()
.local_branches_matching(pattern)
.local_bookmarks_matching(pattern)
.filter(|(_, target)| target.added_ids().any(&is_source_commit))
})?
} else {
repo.view()
.local_branches()
.local_bookmarks()
.filter(|(_, target)| target.added_ids().any(&is_source_commit))
.collect()
};
// Noop matches aren't error, but should be excluded from stats.
branches.retain(|(_, old_target)| old_target.as_normal() != Some(target_commit.id()));
branches
bookmarks.retain(|(_, old_target)| old_target.as_normal() != Some(target_commit.id()));
bookmarks
};
if matched_branches.is_empty() {
writeln!(ui.status(), "No branches to update.")?;
if matched_bookmarks.is_empty() {
writeln!(ui.status(), "No bookmarks to update.")?;
return Ok(());
}
if !args.allow_backwards {
if let Some((name, _)) = matched_branches
if let Some((name, _)) = matched_bookmarks
.iter()
.find(|(_, old_target)| !is_fast_forward(repo.as_ref(), old_target, target_commit.id()))
{
return Err(user_error_with_hint(
format!("Refusing to move branch backwards or sideways: {name}"),
format!("Refusing to move bookmark backwards or sideways: {name}"),
"Use --allow-backwards to allow it.",
));
}
}
let mut tx = workspace_command.start_transaction();
for (name, _) in &matched_branches {
for (name, _) in &matched_bookmarks {
tx.repo_mut()
.set_local_branch_target(name, RefTarget::normal(target_commit.id().clone()));
.set_local_bookmark_target(name, RefTarget::normal(target_commit.id().clone()));
}
if let Some(mut formatter) = ui.status_formatter() {
write!(formatter, "Moved {} branches to ", matched_branches.len())?;
write!(formatter, "Moved {} bookmarks to ", matched_bookmarks.len())?;
tx.write_commit_summary(formatter.as_mut(), &target_commit)?;
writeln!(formatter)?;
}
if matched_branches.len() > 1 && args.names.is_empty() {
if matched_bookmarks.len() > 1 && args.names.is_empty() {
writeln!(
ui.hint_default(),
"Specify branch by name to update just one of the branches."
"Specify bookmark by name to update just one of the bookmarks."
)?;
}
tx.finish(
ui,
format!(
"point branch {names} to commit {id}",
names = matched_branches.iter().map(|(name, _)| name).join(", "),
"point bookmark {names} to commit {id}",
names = matched_bookmarks.iter().map(|(name, _)| name).join(", "),
id = target_commit.id().hex()
),
)?;

View file

@ -0,0 +1,93 @@
// Copyright 2020-2023 The Jujutsu Authors
//
// 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 jj_lib::op_store::RefTarget;
use super::has_tracked_remote_bookmarks;
use crate::cli_util::CommandHelper;
use crate::command_error::user_error;
use crate::command_error::CommandError;
use crate::ui::Ui;
/// Rename `old` bookmark name to `new` bookmark name
///
/// The new bookmark name points at the same commit as the old bookmark name.
#[derive(clap::Args, Clone, Debug)]
pub struct BookmarkRenameArgs {
/// The old name of the bookmark
old: String,
/// The new name of the bookmark
new: String,
}
pub fn cmd_bookmark_rename(
ui: &mut Ui,
command: &CommandHelper,
args: &BookmarkRenameArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let view = workspace_command.repo().view();
let old_bookmark = &args.old;
let ref_target = view.get_local_bookmark(old_bookmark).clone();
if ref_target.is_absent() {
return Err(user_error(format!("No such bookmark: {old_bookmark}")));
}
let new_bookmark = &args.new;
if view.get_local_bookmark(new_bookmark).is_present() {
return Err(user_error(format!(
"Bookmark already exists: {new_bookmark}"
)));
}
let mut tx = workspace_command.start_transaction();
tx.repo_mut()
.set_local_bookmark_target(new_bookmark, ref_target);
tx.repo_mut()
.set_local_bookmark_target(old_bookmark, RefTarget::absent());
tx.finish(
ui,
format!("rename bookmark {old_bookmark} to {new_bookmark}"),
)?;
let view = workspace_command.repo().view();
if has_tracked_remote_bookmarks(view, old_bookmark) {
writeln!(
ui.warning_default(),
"Tracked remote bookmarks for bookmark {old_bookmark} were not renamed.",
)?;
writeln!(
ui.hint_default(),
"To rename the bookmark on the remote, you can `jj git push --bookmark \
{old_bookmark}` first (to delete it on the remote), and then `jj git push --bookmark \
{new_bookmark}`. `jj git push --all` would also be sufficient."
)?;
}
if has_tracked_remote_bookmarks(view, new_bookmark) {
// This isn't an error because bookmark renaming can't be propagated to
// the remote immediately. "rename old new && rename new old" should be
// allowed even if the original old bookmark had tracked remotes.
writeln!(
ui.warning_default(),
"Tracked remote bookmarks for bookmark {new_bookmark} exist."
)?;
writeln!(
ui.hint_default(),
"Run `jj bookmark untrack 'glob:{new_bookmark}@*'` to disassociate them."
)?;
}
Ok(())
}

View file

@ -16,7 +16,7 @@ use clap::builder::NonEmptyStringValueParser;
use jj_lib::object_id::ObjectId as _;
use jj_lib::op_store::RefTarget;
use super::has_tracked_remote_branches;
use super::has_tracked_remote_bookmarks;
use super::is_fast_forward;
use crate::cli_util::CommandHelper;
use crate::cli_util::RevisionArg;
@ -24,88 +24,90 @@ use crate::command_error::user_error_with_hint;
use crate::command_error::CommandError;
use crate::ui::Ui;
/// Create or update a branch to point to a certain commit
/// Create or update a bookmark to point to a certain commit
#[derive(clap::Args, Clone, Debug)]
pub struct BranchSetArgs {
/// The branch's target revision
pub struct BookmarkSetArgs {
/// The bookmark's target revision
#[arg(long, short, visible_alias = "to")]
revision: Option<RevisionArg>,
/// Allow moving the branch backwards or sideways
/// Allow moving the bookmark backwards or sideways
#[arg(long, short = 'B')]
allow_backwards: bool,
/// The branches to update
/// The bookmarks to update
#[arg(required = true, value_parser = NonEmptyStringValueParser::new())]
names: Vec<String>,
}
pub fn cmd_branch_set(
pub fn cmd_bookmark_set(
ui: &mut Ui,
command: &CommandHelper,
args: &BranchSetArgs,
args: &BookmarkSetArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let target_commit =
workspace_command.resolve_single_rev(args.revision.as_ref().unwrap_or(&RevisionArg::AT))?;
let repo = workspace_command.repo().as_ref();
let branch_names = &args.names;
let mut new_branch_count = 0;
let mut moved_branch_count = 0;
for name in branch_names {
let old_target = repo.view().get_local_branch(name);
// If a branch is absent locally but is still tracking remote branches,
// we are resurrecting the local branch, not "creating" a new branch.
if old_target.is_absent() && !has_tracked_remote_branches(repo.view(), name) {
new_branch_count += 1;
let bookmark_names = &args.names;
let mut new_bookmark_count = 0;
let mut moved_bookmark_count = 0;
for name in bookmark_names {
let old_target = repo.view().get_local_bookmark(name);
// If a bookmark is absent locally but is still tracking remote bookmarks,
// we are resurrecting the local bookmark, not "creating" a new bookmark.
if old_target.is_absent() && !has_tracked_remote_bookmarks(repo.view(), name) {
new_bookmark_count += 1;
} else if old_target.as_normal() != Some(target_commit.id()) {
moved_branch_count += 1;
moved_bookmark_count += 1;
}
if !args.allow_backwards && !is_fast_forward(repo, old_target, target_commit.id()) {
return Err(user_error_with_hint(
format!("Refusing to move branch backwards or sideways: {name}"),
format!("Refusing to move bookmark backwards or sideways: {name}"),
"Use --allow-backwards to allow it.",
));
}
}
let mut tx = workspace_command.start_transaction();
for branch_name in branch_names {
tx.repo_mut()
.set_local_branch_target(branch_name, RefTarget::normal(target_commit.id().clone()));
for bookmark_name in bookmark_names {
tx.repo_mut().set_local_bookmark_target(
bookmark_name,
RefTarget::normal(target_commit.id().clone()),
);
}
if let Some(mut formatter) = ui.status_formatter() {
if new_branch_count > 0 {
if new_bookmark_count > 0 {
write!(
formatter,
"Created {new_branch_count} branches pointing to "
"Created {new_bookmark_count} bookmarks pointing to "
)?;
tx.write_commit_summary(formatter.as_mut(), &target_commit)?;
writeln!(formatter)?;
}
if moved_branch_count > 0 {
write!(formatter, "Moved {moved_branch_count} branches to ")?;
if moved_bookmark_count > 0 {
write!(formatter, "Moved {moved_bookmark_count} bookmarks to ")?;
tx.write_commit_summary(formatter.as_mut(), &target_commit)?;
writeln!(formatter)?;
}
}
if branch_names.len() > 1 && args.revision.is_none() {
if bookmark_names.len() > 1 && args.revision.is_none() {
writeln!(ui.hint_default(), "Use -r to specify the target revision.")?;
}
if new_branch_count > 0 {
if new_bookmark_count > 0 {
// TODO: delete this hint in jj 0.25+
writeln!(
ui.hint_default(),
"Consider using `jj branch move` if your intention was to move existing branches."
"Consider using `jj bookmark move` if your intention was to move existing bookmarks."
)?;
}
tx.finish(
ui,
format!(
"point branch {names} to commit {id}",
names = branch_names.join(", "),
"point bookmark {names} to commit {id}",
names = bookmark_names.join(", "),
id = target_commit.id().hex()
),
)?;

View file

@ -16,45 +16,45 @@ use std::collections::HashMap;
use itertools::Itertools as _;
use super::find_remote_branches;
use super::find_remote_bookmarks;
use crate::cli_util::CommandHelper;
use crate::cli_util::RemoteBranchNamePattern;
use crate::cli_util::RemoteBookmarkNamePattern;
use crate::command_error::CommandError;
use crate::commit_templater::CommitTemplateLanguage;
use crate::commit_templater::RefName;
use crate::ui::Ui;
/// Start tracking given remote branches
/// Start tracking given remote bookmarks
///
/// A tracking remote branch will be imported as a local branch of the same
/// name. Changes to it will propagate to the existing local branch on future
/// A tracking remote bookmark will be imported as a local bookmark of the same
/// name. Changes to it will propagate to the existing local bookmark on future
/// pulls.
#[derive(clap::Args, Clone, Debug)]
pub struct BranchTrackArgs {
/// Remote branches to track
pub struct BookmarkTrackArgs {
/// Remote bookmarks to track
///
/// By default, the specified name matches exactly. Use `glob:` prefix to
/// select branches by wildcard pattern. For details, see
/// select bookmarks by wildcard pattern. For details, see
/// https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
///
/// Examples: branch@remote, glob:main@*, glob:jjfan-*@upstream
/// Examples: bookmark@remote, glob:main@*, glob:jjfan-*@upstream
#[arg(required = true, value_name = "BRANCH@REMOTE")]
names: Vec<RemoteBranchNamePattern>,
names: Vec<RemoteBookmarkNamePattern>,
}
pub fn cmd_branch_track(
pub fn cmd_bookmark_track(
ui: &mut Ui,
command: &CommandHelper,
args: &BranchTrackArgs,
args: &BookmarkTrackArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let view = workspace_command.repo().view();
let mut names = Vec::new();
for (name, remote_ref) in find_remote_branches(view, &args.names)? {
for (name, remote_ref) in find_remote_bookmarks(view, &args.names)? {
if remote_ref.is_tracking() {
writeln!(
ui.warning_default(),
"Remote branch already tracked: {name}"
"Remote bookmark already tracked: {name}"
)?;
} else {
names.push(name);
@ -63,21 +63,21 @@ pub fn cmd_branch_track(
let mut tx = workspace_command.start_transaction();
for name in &names {
tx.repo_mut()
.track_remote_branch(&name.branch, &name.remote);
.track_remote_bookmark(&name.bookmark, &name.remote);
}
if !names.is_empty() {
writeln!(
ui.status(),
"Started tracking {} remote branches.",
"Started tracking {} remote bookmarks.",
names.len()
)?;
}
tx.finish(
ui,
format!("track remote branch {}", names.iter().join(", ")),
format!("track remote bookmark {}", names.iter().join(", ")),
)?;
//show conflicted branches if there are some
//show conflicted bookmarks if there are some
if let Some(mut formatter) = ui.status_formatter() {
let template = {
@ -85,39 +85,39 @@ pub fn cmd_branch_track(
let text = command
.settings()
.config()
.get::<String>("templates.branch_list")?;
.get::<String>("templates.bookmark_list")?;
workspace_command
.parse_template(&language, &text, CommitTemplateLanguage::wrap_ref_name)?
.labeled("branch_list")
.labeled("bookmark_list")
};
let mut remote_per_branch: HashMap<&str, Vec<&str>> = HashMap::new();
let mut remote_per_bookmark: HashMap<&str, Vec<&str>> = HashMap::new();
for n in names.iter() {
remote_per_branch
.entry(&n.branch)
remote_per_bookmark
.entry(&n.bookmark)
.or_default()
.push(&n.remote);
}
let branches_to_list =
let bookmarks_to_list =
workspace_command
.repo()
.view()
.branches()
.bookmarks()
.filter(|(name, target)| {
remote_per_branch.contains_key(name) && target.local_target.has_conflict()
remote_per_bookmark.contains_key(name) && target.local_target.has_conflict()
});
for (name, branch_target) in branches_to_list {
let local_target = branch_target.local_target;
for (name, bookmark_target) in bookmarks_to_list {
let local_target = bookmark_target.local_target;
let ref_name = RefName::local(
name,
local_target.clone(),
branch_target.remote_refs.iter().map(|x| x.1),
bookmark_target.remote_refs.iter().map(|x| x.1),
);
template.format(&ref_name, formatter.as_mut())?;
for (remote_name, remote_ref) in branch_target.remote_refs {
if remote_per_branch[name].contains(&remote_name) {
for (remote_name, remote_ref) in bookmark_target.remote_refs {
if remote_per_bookmark[name].contains(&remote_name) {
let ref_name =
RefName::remote(name, remote_name, remote_ref.clone(), local_target);
template.format(&ref_name, formatter.as_mut())?;

View file

@ -15,48 +15,49 @@
use itertools::Itertools as _;
use jj_lib::git;
use super::find_remote_branches;
use super::find_remote_bookmarks;
use crate::cli_util::CommandHelper;
use crate::cli_util::RemoteBranchNamePattern;
use crate::cli_util::RemoteBookmarkNamePattern;
use crate::command_error::CommandError;
use crate::ui::Ui;
/// Stop tracking given remote branches
/// Stop tracking given remote bookmarks
///
/// A non-tracking remote branch is just a pointer to the last-fetched remote
/// branch. It won't be imported as a local branch on future pulls.
/// 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.
#[derive(clap::Args, Clone, Debug)]
pub struct BranchUntrackArgs {
/// Remote branches to untrack
pub struct BookmarkUntrackArgs {
/// Remote bookmarks to untrack
///
/// By default, the specified name matches exactly. Use `glob:` prefix to
/// select branches by wildcard pattern. For details, see
/// select bookmarks by wildcard pattern. For details, see
/// https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
///
/// Examples: branch@remote, glob:main@*, glob:jjfan-*@upstream
/// Examples: bookmark@remote, glob:main@*, glob:jjfan-*@upstream
#[arg(required = true, value_name = "BRANCH@REMOTE")]
names: Vec<RemoteBranchNamePattern>,
names: Vec<RemoteBookmarkNamePattern>,
}
pub fn cmd_branch_untrack(
pub fn cmd_bookmark_untrack(
ui: &mut Ui,
command: &CommandHelper,
args: &BranchUntrackArgs,
args: &BookmarkUntrackArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let view = workspace_command.repo().view();
let mut names = Vec::new();
for (name, remote_ref) in find_remote_branches(view, &args.names)? {
for (name, remote_ref) in find_remote_bookmarks(view, &args.names)? {
if name.remote == git::REMOTE_NAME_FOR_LOCAL_GIT_REPO {
// This restriction can be lifted if we want to support untracked @git branches.
// This restriction can be lifted if we want to support untracked @git
// bookmarks.
writeln!(
ui.warning_default(),
"Git-tracking branch cannot be untracked: {name}"
"Git-tracking bookmark cannot be untracked: {name}"
)?;
} else if !remote_ref.is_tracking() {
writeln!(
ui.warning_default(),
"Remote branch not tracked yet: {name}"
"Remote bookmark not tracked yet: {name}"
)?;
} else {
names.push(name);
@ -65,18 +66,18 @@ pub fn cmd_branch_untrack(
let mut tx = workspace_command.start_transaction();
for name in &names {
tx.repo_mut()
.untrack_remote_branch(&name.branch, &name.remote);
.untrack_remote_bookmark(&name.bookmark, &name.remote);
}
if !names.is_empty() {
writeln!(
ui.status(),
"Stopped tracking {} remote branches.",
"Stopped tracking {} remote bookmarks.",
names.len()
)?;
}
tx.finish(
ui,
format!("untrack remote branch {}", names.iter().join(", ")),
format!("untrack remote bookmark {}", names.iter().join(", ")),
)?;
Ok(())
}

View file

@ -1,194 +0,0 @@
// Copyright 2020-2023 The Jujutsu Authors
//
// 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.
mod create;
mod delete;
mod forget;
mod list;
mod r#move;
mod rename;
mod set;
mod track;
mod untrack;
use itertools::Itertools as _;
use jj_lib::backend::CommitId;
use jj_lib::git;
use jj_lib::op_store::RefTarget;
use jj_lib::op_store::RemoteRef;
use jj_lib::repo::Repo;
use jj_lib::str_util::StringPattern;
use jj_lib::view::View;
use self::create::cmd_branch_create;
use self::create::BranchCreateArgs;
use self::delete::cmd_branch_delete;
use self::delete::BranchDeleteArgs;
use self::forget::cmd_branch_forget;
use self::forget::BranchForgetArgs;
use self::list::cmd_branch_list;
use self::list::BranchListArgs;
use self::r#move::cmd_branch_move;
use self::r#move::BranchMoveArgs;
use self::rename::cmd_branch_rename;
use self::rename::BranchRenameArgs;
use self::set::cmd_branch_set;
use self::set::BranchSetArgs;
use self::track::cmd_branch_track;
use self::track::BranchTrackArgs;
use self::untrack::cmd_branch_untrack;
use self::untrack::BranchUntrackArgs;
use crate::cli_util::CommandHelper;
use crate::cli_util::RemoteBranchName;
use crate::cli_util::RemoteBranchNamePattern;
use crate::command_error::user_error;
use crate::command_error::CommandError;
use crate::ui::Ui;
/// Manage branches
///
/// For information about branches, see
/// https://martinvonz.github.io/jj/latest/branches/.
#[derive(clap::Subcommand, Clone, Debug)]
pub enum BranchCommand {
#[command(visible_alias("c"))]
Create(BranchCreateArgs),
#[command(visible_alias("d"))]
Delete(BranchDeleteArgs),
#[command(visible_alias("f"))]
Forget(BranchForgetArgs),
#[command(visible_alias("l"))]
List(BranchListArgs),
#[command(visible_alias("m"))]
Move(BranchMoveArgs),
#[command(visible_alias("r"))]
Rename(BranchRenameArgs),
#[command(visible_alias("s"))]
Set(BranchSetArgs),
#[command(visible_alias("t"))]
Track(BranchTrackArgs),
Untrack(BranchUntrackArgs),
}
pub fn cmd_branch(
ui: &mut Ui,
command: &CommandHelper,
subcommand: &BranchCommand,
) -> Result<(), CommandError> {
match subcommand {
BranchCommand::Create(args) => cmd_branch_create(ui, command, args),
BranchCommand::Delete(args) => cmd_branch_delete(ui, command, args),
BranchCommand::Forget(args) => cmd_branch_forget(ui, command, args),
BranchCommand::List(args) => cmd_branch_list(ui, command, args),
BranchCommand::Move(args) => cmd_branch_move(ui, command, args),
BranchCommand::Rename(args) => cmd_branch_rename(ui, command, args),
BranchCommand::Set(args) => cmd_branch_set(ui, command, args),
BranchCommand::Track(args) => cmd_branch_track(ui, command, args),
BranchCommand::Untrack(args) => cmd_branch_untrack(ui, command, args),
}
}
fn find_local_branches<'a>(
view: &'a View,
name_patterns: &[StringPattern],
) -> Result<Vec<(&'a str, &'a RefTarget)>, CommandError> {
find_branches_with(name_patterns, |pattern| {
view.local_branches_matching(pattern)
})
}
fn find_branches_with<'a, 'b, V, I: Iterator<Item = (&'a str, V)>>(
name_patterns: &'b [StringPattern],
mut find_matches: impl FnMut(&'b StringPattern) -> I,
) -> Result<Vec<I::Item>, CommandError> {
let mut matching_branches: Vec<I::Item> = vec![];
let mut unmatched_patterns = vec![];
for pattern in name_patterns {
let mut matches = find_matches(pattern).peekable();
if matches.peek().is_none() {
unmatched_patterns.push(pattern);
}
matching_branches.extend(matches);
}
match &unmatched_patterns[..] {
[] => {
matching_branches.sort_unstable_by_key(|(name, _)| *name);
matching_branches.dedup_by_key(|(name, _)| *name);
Ok(matching_branches)
}
[pattern] if pattern.is_exact() => Err(user_error(format!("No such branch: {pattern}"))),
patterns => Err(user_error(format!(
"No matching branches for patterns: {}",
patterns.iter().join(", ")
))),
}
}
fn find_remote_branches<'a>(
view: &'a View,
name_patterns: &[RemoteBranchNamePattern],
) -> Result<Vec<(RemoteBranchName, &'a RemoteRef)>, CommandError> {
let mut matching_branches = vec![];
let mut unmatched_patterns = vec![];
for pattern in name_patterns {
let mut matches = view
.remote_branches_matching(&pattern.branch, &pattern.remote)
.map(|((branch, remote), remote_ref)| {
let name = RemoteBranchName {
branch: branch.to_owned(),
remote: remote.to_owned(),
};
(name, remote_ref)
})
.peekable();
if matches.peek().is_none() {
unmatched_patterns.push(pattern);
}
matching_branches.extend(matches);
}
match &unmatched_patterns[..] {
[] => {
matching_branches.sort_unstable_by(|(name1, _), (name2, _)| name1.cmp(name2));
matching_branches.dedup_by(|(name1, _), (name2, _)| name1 == name2);
Ok(matching_branches)
}
[pattern] if pattern.is_exact() => {
Err(user_error(format!("No such remote branch: {pattern}")))
}
patterns => Err(user_error(format!(
"No matching remote branches for patterns: {}",
patterns.iter().join(", ")
))),
}
}
/// Whether or not the `branch` has any tracked remotes (i.e. is a tracking
/// local branch.)
fn has_tracked_remote_branches(view: &View, branch: &str) -> bool {
view.remote_branches_matching(&StringPattern::exact(branch), &StringPattern::everything())
.filter(|&((_, remote_name), _)| remote_name != git::REMOTE_NAME_FOR_LOCAL_GIT_REPO)
.any(|(_, remote_ref)| remote_ref.is_tracking())
}
fn is_fast_forward(repo: &dyn Repo, old_target: &RefTarget, new_target_id: &CommitId) -> bool {
if old_target.is_present() {
// Strictly speaking, "all" old targets should be ancestors, but we allow
// conflict resolution by setting branch to "any" of the old target descendants.
old_target
.added_ids()
.any(|old| repo.index().is_ancestor(old, new_target_id))
} else {
true
}
}

View file

@ -1,88 +0,0 @@
// Copyright 2020-2023 The Jujutsu Authors
//
// 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 jj_lib::op_store::RefTarget;
use super::has_tracked_remote_branches;
use crate::cli_util::CommandHelper;
use crate::command_error::user_error;
use crate::command_error::CommandError;
use crate::ui::Ui;
/// Rename `old` branch name to `new` branch name
///
/// The new branch name points at the same commit as the old branch name.
#[derive(clap::Args, Clone, Debug)]
pub struct BranchRenameArgs {
/// The old name of the branch
old: String,
/// The new name of the branch
new: String,
}
pub fn cmd_branch_rename(
ui: &mut Ui,
command: &CommandHelper,
args: &BranchRenameArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let view = workspace_command.repo().view();
let old_branch = &args.old;
let ref_target = view.get_local_branch(old_branch).clone();
if ref_target.is_absent() {
return Err(user_error(format!("No such branch: {old_branch}")));
}
let new_branch = &args.new;
if view.get_local_branch(new_branch).is_present() {
return Err(user_error(format!("Branch already exists: {new_branch}")));
}
let mut tx = workspace_command.start_transaction();
tx.repo_mut()
.set_local_branch_target(new_branch, ref_target);
tx.repo_mut()
.set_local_branch_target(old_branch, RefTarget::absent());
tx.finish(ui, format!("rename branch {old_branch} to {new_branch}"))?;
let view = workspace_command.repo().view();
if has_tracked_remote_branches(view, old_branch) {
writeln!(
ui.warning_default(),
"Tracked remote branches for branch {old_branch} were not renamed.",
)?;
writeln!(
ui.hint_default(),
"To rename the branch on the remote, you can `jj git push --branch {old_branch}` \
first (to delete it on the remote), and then `jj git push --branch {new_branch}`. \
`jj git push --all` would also be sufficient."
)?;
}
if has_tracked_remote_branches(view, new_branch) {
// This isn't an error because branch renaming can't be propagated to
// the remote immediately. "rename old new && rename new old" should be
// allowed even if the original old branch had tracked remotes.
writeln!(
ui.warning_default(),
"Tracked remote branches for branch {new_branch} exist."
)?;
writeln!(
ui.hint_default(),
"Run `jj branch untrack 'glob:{new_branch}@*'` to disassociate them."
)?;
}
Ok(())
}

View file

@ -67,7 +67,7 @@ pub(crate) fn cmd_commit(
let matcher = workspace_command
.parse_file_patterns(&args.paths)?
.to_matcher();
let advanceable_branches = workspace_command.get_advanceable_branches(commit.parent_ids())?;
let advanceable_branches = workspace_command.get_advanceable_bookmarks(commit.parent_ids())?;
let diff_selector =
workspace_command.diff_selector(ui, args.tool.as_deref(), args.interactive)?;
let mut tx = workspace_command.start_transaction();
@ -132,7 +132,7 @@ new working-copy commit.
.write()?;
// Does nothing if there's no branches to advance.
tx.advance_branches(advanceable_branches, new_commit.id());
tx.advance_bookmarks(advanceable_branches, new_commit.id());
for workspace_id in workspace_ids {
tx.repo_mut().edit(workspace_id, &new_wc_commit).unwrap();

View file

@ -172,13 +172,13 @@ pub fn cmd_git_clone(
let default_branch_remote_ref = workspace_command
.repo()
.view()
.get_remote_branch(default_branch, remote_name);
.get_remote_bookmark(default_branch, remote_name);
if let Some(commit_id) = default_branch_remote_ref.target.as_normal().cloned() {
let mut checkout_tx = workspace_command.start_transaction();
// For convenience, create local branch as Git would do.
checkout_tx
.repo_mut()
.track_remote_branch(default_branch, remote_name);
.track_remote_bookmark(default_branch, remote_name);
if let Ok(commit) = checkout_tx.repo().store().get_commit(&commit_id) {
checkout_tx.check_out(&commit)?;
}

View file

@ -25,7 +25,7 @@ use jj_lib::repo::ReadonlyRepo;
use jj_lib::repo::Repo;
use jj_lib::workspace::Workspace;
use crate::cli_util::print_trackable_remote_branches;
use crate::cli_util::print_trackable_remote_bookmarks;
use crate::cli_util::start_repo_transaction;
use crate::cli_util::CommandHelper;
use crate::cli_util::WorkspaceCommandHelper;
@ -184,7 +184,7 @@ pub fn do_init(
tx.finish(ui, "import git head")?;
}
}
print_trackable_remote_branches(ui, workspace_command.repo().view())?;
print_trackable_remote_bookmarks(ui, workspace_command.repo().view())?;
}
GitInitMode::Internal => {
Workspace::init_internal_git(command.settings(), workspace_root)?;

View file

@ -26,7 +26,7 @@ use jj_lib::git::GitBranchPushTargets;
use jj_lib::git::GitPushError;
use jj_lib::object_id::ObjectId;
use jj_lib::op_store::RefTarget;
use jj_lib::refs::classify_branch_push_action;
use jj_lib::refs::classify_bookmark_push_action;
use jj_lib::refs::BranchPushAction;
use jj_lib::refs::BranchPushUpdate;
use jj_lib::refs::LocalAndRemoteRef;
@ -56,51 +56,52 @@ use crate::ui::Ui;
/// Push to a Git remote
///
/// By default, pushes any branches pointing to
/// `remote_branches(remote=<remote>)..@`. Use `--branch` to push specific
/// branches. Use `--all` to push all branches. Use `--change` to generate
/// branch names based on the change IDs of specific commits.
/// By default, pushes any bookmarks pointing to
/// `remote_bookmarks(remote=<remote>)..@`. Use `--bookmark` to push specific
/// bookmarks. Use `--all` to push all bookmarks. Use `--change` to generate
/// bookmark names based on the change IDs of specific commits.
///
/// Before the command actually moves, creates, or deletes a remote branch, it
/// Before the command actually moves, creates, or deletes a remote bookmark, it
/// makes several [safety checks]. If there is a problem, you may need to run
/// `jj git fetch --remote <remote name>` and/or resolve some [branch
/// `jj git fetch --remote <remote name>` and/or resolve some [bookmark
/// conflicts].
///
/// [safety checks]:
/// https://martinvonz.github.io/jj/latest/branches/#pushing-branches-safety-checks
/// https://martinvonz.github.io/jj/latest/bookmarks/#pushing-bookmarks-safety-checks
///
/// [branch conflicts]:
/// https://martinvonz.github.io/jj/latest/branches/#conflicts
/// [bookmark conflicts]:
/// https://martinvonz.github.io/jj/latest/bookmarks/#conflicts
#[derive(clap::Args, Clone, Debug)]
#[command(group(ArgGroup::new("specific").args(&["branch", "change", "revisions"]).multiple(true)))]
#[command(group(ArgGroup::new("specific").args(&["bookmark", "change", "revisions"]).multiple(true)))]
#[command(group(ArgGroup::new("what").args(&["all", "deleted", "tracked"]).conflicts_with("specific")))]
pub struct GitPushArgs {
/// The remote to push to (only named remotes are supported)
#[arg(long)]
remote: Option<String>,
/// Push only this branch, or branches matching a pattern (can be repeated)
/// Push only this bookmark, or bookmarks matching a pattern (can be
/// repeated)
///
/// By default, the specified name matches exactly. Use `glob:` prefix to
/// select branches by wildcard pattern. For details, see
/// select bookmarks by wildcard pattern. For details, see
/// https://martinvonz.github.io/jj/latest/revsets#string-patterns.
#[arg(long, short, value_parser = StringPattern::parse)]
branch: Vec<StringPattern>,
/// Push all branches (including deleted branches)
bookmark: Vec<StringPattern>,
/// Push all bookmarks (including deleted bookmarks)
#[arg(long)]
all: bool,
/// Push all tracked branches (including deleted branches)
/// Push all tracked bookmarks (including deleted bookmarks)
///
/// This usually means that the branch was already pushed to or fetched from
/// the relevant remote. For details, see
/// https://martinvonz.github.io/jj/latest/branches#remotes-and-tracked-branches
/// This usually means that the bookmark was already pushed to or fetched
/// from the relevant remote. For details, see
/// https://martinvonz.github.io/jj/latest/bookmarks#remotes-and-tracked-bookmarks
#[arg(long)]
tracked: bool,
/// Push all deleted branches
/// Push all deleted bookmarks
///
/// Only tracked branches can be successfully deleted on the remote. A
/// warning will be printed if any untracked branches on the remote
/// correspond to missing local branches.
/// Only tracked bookmarks can be successfully deleted on the remote. A
/// warning will be printed if any untracked bookmarks on the remote
/// correspond to missing local bookmarks.
#[arg(long)]
deleted: bool,
/// Allow pushing commits with empty descriptions
@ -109,10 +110,10 @@ pub struct GitPushArgs {
/// Allow pushing commits that are private
#[arg(long)]
allow_private: bool,
/// Push branches pointing to these commits (can be repeated)
/// Push bookmarks pointing to these commits (can be repeated)
#[arg(long, short)]
revisions: Vec<RevisionArg>,
/// Push this commit by creating a branch based on its change ID (can be
/// Push this commit by creating a bookmark based on its change ID (can be
/// repeated)
#[arg(long, short)]
change: Vec<RevisionArg>,
@ -121,10 +122,10 @@ pub struct GitPushArgs {
dry_run: bool,
}
fn make_branch_term(branch_names: &[impl fmt::Display]) -> String {
match branch_names {
[branch_name] => format!("branch {}", branch_name),
branch_names => format!("branches {}", branch_names.iter().join(", ")),
fn make_bookmark_term(bookmark_names: &[impl fmt::Display]) -> String {
match bookmark_names {
[bookmark_name] => format!("bookmark {}", bookmark_name),
bookmark_names => format!("bookmarks {}", bookmark_names.iter().join(", ")),
}
}
@ -156,85 +157,85 @@ pub fn cmd_git_push(
let tx_description;
let mut branch_updates = vec![];
if args.all {
for (branch_name, targets) in repo.view().local_remote_branches(&remote) {
match classify_branch_update(branch_name, &remote, targets) {
Ok(Some(update)) => branch_updates.push((branch_name.to_owned(), update)),
for (bookmark_name, targets) in repo.view().local_remote_bookmarks(&remote) {
match classify_bookmark_update(bookmark_name, &remote, targets) {
Ok(Some(update)) => branch_updates.push((bookmark_name.to_owned(), update)),
Ok(None) => {}
Err(reason) => reason.print(ui)?,
}
}
tx_description = format!("push all branches to git remote {remote}");
tx_description = format!("push all bookmarks to git remote {remote}");
} else if args.tracked {
for (branch_name, targets) in repo.view().local_remote_branches(&remote) {
for (bookmark_name, targets) in repo.view().local_remote_bookmarks(&remote) {
if !targets.remote_ref.is_tracking() {
continue;
}
match classify_branch_update(branch_name, &remote, targets) {
Ok(Some(update)) => branch_updates.push((branch_name.to_owned(), update)),
match classify_bookmark_update(bookmark_name, &remote, targets) {
Ok(Some(update)) => branch_updates.push((bookmark_name.to_owned(), update)),
Ok(None) => {}
Err(reason) => reason.print(ui)?,
}
}
tx_description = format!("push all tracked branches to git remote {remote}");
tx_description = format!("push all tracked bookmarks to git remote {remote}");
} else if args.deleted {
for (branch_name, targets) in repo.view().local_remote_branches(&remote) {
for (bookmark_name, targets) in repo.view().local_remote_bookmarks(&remote) {
if targets.local_target.is_present() {
continue;
}
match classify_branch_update(branch_name, &remote, targets) {
Ok(Some(update)) => branch_updates.push((branch_name.to_owned(), update)),
match classify_bookmark_update(bookmark_name, &remote, targets) {
Ok(Some(update)) => branch_updates.push((bookmark_name.to_owned(), update)),
Ok(None) => {}
Err(reason) => reason.print(ui)?,
}
}
tx_description = format!("push all deleted branches to git remote {remote}");
tx_description = format!("push all deleted bookmarks to git remote {remote}");
} else {
let mut seen_branches: HashSet<&str> = HashSet::new();
let mut seen_bookmarks: HashSet<&str> = HashSet::new();
// Process --change branches first because matching branches can be moved.
let change_branch_names = update_change_branches(
// Process --change bookmarks first because matching bookmarks can be moved.
let change_bookmark_names = update_change_bookmarks(
ui,
&mut tx,
&args.change,
&command.settings().push_branch_prefix(),
&command.settings().push_bookmark_prefix(),
)?;
let change_branches = change_branch_names.iter().map(|branch_name| {
let change_bookmarks = change_bookmark_names.iter().map(|bookmark_name| {
let targets = LocalAndRemoteRef {
local_target: tx.repo().view().get_local_branch(branch_name),
remote_ref: tx.repo().view().get_remote_branch(branch_name, &remote),
local_target: tx.repo().view().get_local_bookmark(bookmark_name),
remote_ref: tx.repo().view().get_remote_bookmark(bookmark_name, &remote),
};
(branch_name.as_ref(), targets)
(bookmark_name.as_ref(), targets)
});
let branches_by_name = find_branches_to_push(repo.view(), &args.branch, &remote)?;
for (branch_name, targets) in change_branches.chain(branches_by_name.iter().copied()) {
if !seen_branches.insert(branch_name) {
let bookmarks_by_name = find_bookmarks_to_push(repo.view(), &args.bookmark, &remote)?;
for (bookmark_name, targets) in change_bookmarks.chain(bookmarks_by_name.iter().copied()) {
if !seen_bookmarks.insert(bookmark_name) {
continue;
}
match classify_branch_update(branch_name, &remote, targets) {
Ok(Some(update)) => branch_updates.push((branch_name.to_owned(), update)),
match classify_bookmark_update(bookmark_name, &remote, targets) {
Ok(Some(update)) => branch_updates.push((bookmark_name.to_owned(), update)),
Ok(None) => writeln!(
ui.status(),
"Branch {branch_name}@{remote} already matches {branch_name}",
"Branch {bookmark_name}@{remote} already matches {bookmark_name}",
)?,
Err(reason) => return Err(reason.into()),
}
}
let use_default_revset =
args.branch.is_empty() && args.change.is_empty() && args.revisions.is_empty();
let branches_targeted = find_branches_targeted_by_revisions(
args.bookmark.is_empty() && args.change.is_empty() && args.revisions.is_empty();
let bookmarks_targeted = find_bookmarks_targeted_by_revisions(
ui,
tx.base_workspace_helper(),
&remote,
&args.revisions,
use_default_revset,
)?;
for &(branch_name, targets) in &branches_targeted {
if !seen_branches.insert(branch_name) {
for &(bookmark_name, targets) in &bookmarks_targeted {
if !seen_bookmarks.insert(bookmark_name) {
continue;
}
match classify_branch_update(branch_name, &remote, targets) {
Ok(Some(update)) => branch_updates.push((branch_name.to_owned(), update)),
match classify_bookmark_update(bookmark_name, &remote, targets) {
Ok(Some(update)) => branch_updates.push((bookmark_name.to_owned(), update)),
Ok(None) => {}
Err(reason) => reason.print(ui)?,
}
@ -242,10 +243,10 @@ pub fn cmd_git_push(
tx_description = format!(
"push {} to git remote {}",
make_branch_term(
make_bookmark_term(
&branch_updates
.iter()
.map(|(branch, _)| branch.as_str())
.map(|(bookmark, _)| bookmark.as_str())
.collect_vec()
),
&remote
@ -256,8 +257,8 @@ pub fn cmd_git_push(
return Ok(());
}
let mut branch_push_direction = HashMap::new();
for (branch_name, update) in &branch_updates {
let mut bookmark_push_direction = HashMap::new();
for (bookmark_name, update) in &branch_updates {
let BranchPushUpdate {
old_target: Some(old_target),
new_target: Some(new_target),
@ -266,8 +267,8 @@ pub fn cmd_git_push(
continue;
};
assert_ne!(old_target, new_target);
branch_push_direction.insert(
branch_name.to_string(),
bookmark_push_direction.insert(
bookmark_name.to_string(),
if repo.index().is_ancestor(old_target, new_target) {
BranchMoveDirection::Forward
} else if repo.index().is_ancestor(new_target, old_target) {
@ -281,25 +282,26 @@ pub fn cmd_git_push(
validate_commits_ready_to_push(&branch_updates, &remote, &tx, command, args)?;
writeln!(ui.status(), "Branch changes to push to {}:", &remote)?;
for (branch_name, update) in &branch_updates {
for (bookmark_name, update) in &branch_updates {
match (&update.old_target, &update.new_target) {
(Some(old_target), Some(new_target)) => {
let old = short_commit_hash(old_target);
let new = short_commit_hash(new_target);
// TODO(ilyagr): Add color. Once there is color, "Move branch ... sideways" may
// read more naturally than "Move sideways branch ...". Without color, it's hard
// to see at a glance if one branch among many was moved sideways (say).
// TODO: People on Discord suggest "Move branch ... forward by n commits",
// possibly "Move branch ... sideways (X forward, Y back)".
let msg = match branch_push_direction.get(branch_name).unwrap() {
// TODO(ilyagr): Add color. Once there is color, "Move bookmark ... sideways"
// may read more naturally than "Move sideways bookmark ...".
// Without color, it's hard to see at a glance if one bookmark
// among many was moved sideways (say). TODO: People on Discord
// suggest "Move bookmark ... forward by n commits",
// possibly "Move bookmark ... sideways (X forward, Y back)".
let msg = match bookmark_push_direction.get(bookmark_name).unwrap() {
BranchMoveDirection::Forward => {
format!("Move forward branch {branch_name} from {old} to {new}")
format!("Move forward bookmark {bookmark_name} from {old} to {new}")
}
BranchMoveDirection::Backward => {
format!("Move backward branch {branch_name} from {old} to {new}")
format!("Move backward bookmark {bookmark_name} from {old} to {new}")
}
BranchMoveDirection::Sideways => {
format!("Move sideways branch {branch_name} from {old} to {new}")
format!("Move sideways bookmark {bookmark_name} from {old} to {new}")
}
};
writeln!(ui.status(), " {msg}")?;
@ -307,19 +309,19 @@ pub fn cmd_git_push(
(Some(old_target), None) => {
writeln!(
ui.status(),
" Delete branch {branch_name} from {}",
" Delete bookmark {bookmark_name} from {}",
short_commit_hash(old_target)
)?;
}
(None, Some(new_target)) => {
writeln!(
ui.status(),
" Add branch {branch_name} to {}",
" Add bookmark {bookmark_name} to {}",
short_commit_hash(new_target)
)?;
}
(None, None) => {
panic!("Not pushing any change to branch {branch_name}");
panic!("Not pushing any change to bookmark {bookmark_name}");
}
}
}
@ -341,12 +343,12 @@ pub fn cmd_git_push(
GitPushError::InternalGitError(err) => map_git_error(err),
GitPushError::RefInUnexpectedLocation(refs) => user_error_with_hint(
format!(
"Refusing to push a branch that unexpectedly moved on the remote. Affected refs: \
{}",
"Refusing to push a bookmark that unexpectedly moved on the remote. Affected \
refs: {}",
refs.join(", ")
),
"Try fetching from the remote, then make the branch point to where you want it to be, \
and push again.",
"Try fetching from the remote, then make the bookmark point to where you want it to \
be, and push again.",
),
_ => user_error(err),
})?;
@ -358,7 +360,7 @@ pub fn cmd_git_push(
/// Validates that the commits that will be pushed are ready (have authorship
/// information, are not conflicted, etc.)
fn validate_commits_ready_to_push(
branch_updates: &[(String, BranchPushUpdate)],
bookmark_updates: &[(String, BranchPushUpdate)],
remote: &str,
tx: &WorkspaceCommandTransaction,
command: &CommandHelper,
@ -367,13 +369,13 @@ fn validate_commits_ready_to_push(
let workspace_helper = tx.base_workspace_helper();
let repo = workspace_helper.repo();
let new_heads = branch_updates
let new_heads = bookmark_updates
.iter()
.filter_map(|(_, update)| update.new_target.clone())
.collect_vec();
let old_heads = repo
.view()
.remote_branches(remote)
.remote_bookmarks(remote)
.flat_map(|(_, old_head)| old_head.target.added_ids())
.cloned()
.collect_vec();
@ -476,40 +478,42 @@ impl From<RejectedBranchUpdateReason> for CommandError {
}
}
fn classify_branch_update(
branch_name: &str,
fn classify_bookmark_update(
bookmark_name: &str,
remote_name: &str,
targets: LocalAndRemoteRef,
) -> Result<Option<BranchPushUpdate>, RejectedBranchUpdateReason> {
let push_action = classify_branch_push_action(targets);
let push_action = classify_bookmark_push_action(targets);
match push_action {
BranchPushAction::AlreadyMatches => Ok(None),
BranchPushAction::LocalConflicted => Err(RejectedBranchUpdateReason {
message: format!("Branch {branch_name} is conflicted"),
message: format!("Branch {bookmark_name} is conflicted"),
hint: Some(
"Run `jj branch list` to inspect, and use `jj branch set` to fix it up.".to_owned(),
"Run `jj bookmark list` to inspect, and use `jj bookmark set` to fix it up."
.to_owned(),
),
}),
BranchPushAction::RemoteConflicted => Err(RejectedBranchUpdateReason {
message: format!("Branch {branch_name}@{remote_name} is conflicted"),
hint: Some("Run `jj git fetch` to update the conflicted remote branch.".to_owned()),
message: format!("Branch {bookmark_name}@{remote_name} is conflicted"),
hint: Some("Run `jj git fetch` to update the conflicted remote bookmark.".to_owned()),
}),
BranchPushAction::RemoteUntracked => Err(RejectedBranchUpdateReason {
message: format!("Non-tracking remote branch {branch_name}@{remote_name} exists"),
message: format!("Non-tracking remote bookmark {bookmark_name}@{remote_name} exists"),
hint: Some(format!(
"Run `jj branch track {branch_name}@{remote_name}` to import the remote branch."
"Run `jj bookmark track {bookmark_name}@{remote_name}` to import the remote \
bookmark."
)),
}),
BranchPushAction::Update(update) => Ok(Some(update)),
}
}
/// Creates or moves branches based on the change IDs.
fn update_change_branches(
/// Creates or moves bookmarks based on the change IDs.
fn update_change_bookmarks(
ui: &Ui,
tx: &mut WorkspaceCommandTransaction,
changes: &[RevisionArg],
branch_prefix: &str,
bookmark_prefix: &str,
) -> Result<Vec<String>, CommandError> {
if changes.is_empty() {
// NOTE: we don't want resolve_some_revsets_default_single to fail if the
@ -517,71 +521,71 @@ fn update_change_branches(
return Ok(vec![]);
}
let mut branch_names = Vec::new();
let mut bookmark_names = Vec::new();
let workspace_command = tx.base_workspace_helper();
let all_commits = workspace_command.resolve_some_revsets_default_single(changes)?;
for commit in all_commits {
let workspace_command = tx.base_workspace_helper();
let short_change_id = short_change_hash(commit.change_id());
let mut branch_name = format!("{branch_prefix}{}", commit.change_id().hex());
let mut bookmark_name = format!("{bookmark_prefix}{}", commit.change_id().hex());
let view = tx.base_repo().view();
if view.get_local_branch(&branch_name).is_absent() {
// A local branch with the full change ID doesn't exist already, so use the
if view.get_local_bookmark(&bookmark_name).is_absent() {
// A local bookmark with the full change ID doesn't exist already, so use the
// short ID if it's not ambiguous (which it shouldn't be most of the time).
if workspace_command
.resolve_single_rev(&RevisionArg::from(short_change_id.clone()))
.is_ok()
{
// Short change ID is not ambiguous, so update the branch name to use it.
branch_name = format!("{branch_prefix}{short_change_id}");
// Short change ID is not ambiguous, so update the bookmark name to use it.
bookmark_name = format!("{bookmark_prefix}{short_change_id}");
};
}
if view.get_local_branch(&branch_name).is_absent() {
if view.get_local_bookmark(&bookmark_name).is_absent() {
writeln!(
ui.status(),
"Creating branch {branch_name} for revision {short_change_id}",
"Creating bookmark {bookmark_name} for revision {short_change_id}",
)?;
}
tx.repo_mut()
.set_local_branch_target(&branch_name, RefTarget::normal(commit.id().clone()));
branch_names.push(branch_name);
.set_local_bookmark_target(&bookmark_name, RefTarget::normal(commit.id().clone()));
bookmark_names.push(bookmark_name);
}
Ok(branch_names)
Ok(bookmark_names)
}
fn find_branches_to_push<'a>(
fn find_bookmarks_to_push<'a>(
view: &'a View,
branch_patterns: &[StringPattern],
bookmark_patterns: &[StringPattern],
remote_name: &str,
) -> Result<Vec<(&'a str, LocalAndRemoteRef<'a>)>, CommandError> {
let mut matching_branches = vec![];
let mut matching_bookmarks = vec![];
let mut unmatched_patterns = vec![];
for pattern in branch_patterns {
for pattern in bookmark_patterns {
let mut matches = view
.local_remote_branches_matching(pattern, remote_name)
.local_remote_bookmarks_matching(pattern, remote_name)
.filter(|(_, targets)| {
// If the remote exists but is not tracking, the absent local shouldn't
// be considered a deleted branch.
// be considered a deleted bookmark.
targets.local_target.is_present() || targets.remote_ref.is_tracking()
})
.peekable();
if matches.peek().is_none() {
unmatched_patterns.push(pattern);
}
matching_branches.extend(matches);
matching_bookmarks.extend(matches);
}
match &unmatched_patterns[..] {
[] => Ok(matching_branches),
[pattern] if pattern.is_exact() => Err(user_error(format!("No such branch: {pattern}"))),
[] => Ok(matching_bookmarks),
[pattern] if pattern.is_exact() => Err(user_error(format!("No such bookmark: {pattern}"))),
patterns => Err(user_error(format!(
"No matching branches for patterns: {}",
"No matching bookmarks for patterns: {}",
patterns.iter().join(", ")
))),
}
}
fn find_branches_targeted_by_revisions<'a>(
fn find_bookmarks_targeted_by_revisions<'a>(
ui: &Ui,
workspace_command: &'a WorkspaceCommandHelper,
remote_name: &str,
@ -593,44 +597,44 @@ fn find_branches_targeted_by_revisions<'a>(
let Some(wc_commit_id) = workspace_command.get_wc_commit_id().cloned() else {
return Err(user_error("Nothing checked out in this workspace"));
};
let current_branches_expression = RevsetExpression::remote_branches(
let current_bookmarks_expression = RevsetExpression::remote_bookmarks(
StringPattern::everything(),
StringPattern::exact(remote_name),
None,
)
.range(&RevsetExpression::commit(wc_commit_id))
.intersection(&RevsetExpression::branches(StringPattern::everything()));
let current_branches_revset =
current_branches_expression.evaluate_programmatic(workspace_command.repo().as_ref())?;
revision_commit_ids.extend(current_branches_revset.iter());
.intersection(&RevsetExpression::bookmarks(StringPattern::everything()));
let current_bookmarks_revset = current_bookmarks_expression
.evaluate_programmatic(workspace_command.repo().as_ref())?;
revision_commit_ids.extend(current_bookmarks_revset.iter());
if revision_commit_ids.is_empty() {
writeln!(
ui.warning_default(),
"No branches found in the default push revset: \
remote_branches(remote={remote_name})..@"
"No bookmarks found in the default push revset: \
remote_bookmarks(remote={remote_name})..@"
)?;
}
}
for rev_arg in revisions {
let mut expression = workspace_command.parse_revset(rev_arg)?;
expression.intersect_with(&RevsetExpression::branches(StringPattern::everything()));
expression.intersect_with(&RevsetExpression::bookmarks(StringPattern::everything()));
let mut commit_ids = expression.evaluate_to_commit_ids()?.peekable();
if commit_ids.peek().is_none() {
writeln!(
ui.warning_default(),
"No branches point to the specified revisions: {rev_arg}"
"No bookmarks point to the specified revisions: {rev_arg}"
)?;
}
revision_commit_ids.extend(commit_ids);
}
let branches_targeted = workspace_command
let bookmarks_targeted = workspace_command
.repo()
.view()
.local_remote_branches(remote_name)
.local_remote_bookmarks(remote_name)
.filter(|(_, targets)| {
let mut local_ids = targets.local_target.added_ids();
local_ids.any(|id| revision_commit_ids.contains(id))
})
.collect_vec();
Ok(branches_targeted)
Ok(bookmarks_targeted)
}

View file

@ -16,7 +16,7 @@ mod abandon;
mod backout;
#[cfg(feature = "bench")]
mod bench;
mod branch;
mod bookmark;
mod checkout;
mod commit;
mod config;
@ -77,8 +77,10 @@ enum Command {
#[command(subcommand)]
Bench(bench::BenchCommand),
#[command(subcommand)]
Branch(branch::BranchCommand),
// TODO: Delete `cat` in jj 0.25+
Bookmark(bookmark::BookmarkCommand),
// TODO: Remove in jj 0.28+
#[command(subcommand, hide = true)]
Branch(bookmark::BookmarkCommand),
#[command(alias = "print", hide = true)]
Cat(file::show::FileShowArgs),
#[command(hide = true)]
@ -110,7 +112,7 @@ enum Command {
Init(init::InitArgs),
Interdiff(interdiff::InterdiffArgs),
Log(log::LogArgs),
/// Merge work from multiple branches (DEPRECATED, use `jj new`)
/// Merge work from multiple bookmarks (DEPRECATED, use `jj new`)
///
/// Unlike most other VCSs, `jj merge` does not implicitly include the
/// working copy revision's parent as one of the parents of the merge;
@ -182,7 +184,11 @@ pub fn run_command(ui: &mut Ui, command_helper: &CommandHelper) -> Result<(), Co
Command::Backout(args) => backout::cmd_backout(ui, command_helper, args),
#[cfg(feature = "bench")]
Command::Bench(args) => bench::cmd_bench(ui, command_helper, args),
Command::Branch(args) => branch::cmd_branch(ui, command_helper, args),
Command::Bookmark(args) => bookmark::cmd_bookmark(ui, command_helper, args),
Command::Branch(args) => {
let cmd = renamed_cmd("branch", "bookmark", bookmark::cmd_bookmark);
cmd(ui, command_helper, args)
}
Command::Cat(args) => {
let cmd = renamed_cmd("cat", "file show", file::show::cmd_file_show);
cmd(ui, command_helper, args)
@ -243,7 +249,7 @@ pub fn run_command(ui: &mut Ui, command_helper: &CommandHelper) -> Result<(), Co
}
/// Wraps deprecated command of `old_name` which has been renamed to `new_name`.
fn renamed_cmd<Args>(
pub(crate) fn renamed_cmd<Args>(
old_name: &'static str,
new_name: &'static str,
cmd: impl Fn(&mut Ui, &CommandHelper, &Args) -> Result<(), CommandError>,

View file

@ -172,7 +172,7 @@ pub(crate) fn cmd_new(
if should_advance_branches {
advance_branches_target = Some(parent_commit_ids[0].clone());
advanceable_branches =
workspace_command.get_advanceable_branches(parent_commits[0].parent_ids())?;
workspace_command.get_advanceable_bookmarks(parent_commits[0].parent_ids())?;
}
};
workspace_command.check_rewritable(children_commits.iter().ids())?;
@ -222,7 +222,7 @@ pub(crate) fn cmd_new(
// Does nothing if there's no branches to advance.
if let Some(target) = advance_branches_target {
tx.advance_branches(advanceable_branches, &target);
tx.advance_bookmarks(advanceable_branches, &target);
}
tx.finish(ui, "new empty commit")?;

View file

@ -300,15 +300,15 @@ pub fn show_op_diff(
}
}
let changed_local_branches = diff_named_ref_targets(
from_repo.view().local_branches(),
to_repo.view().local_branches(),
let changed_local_bookmarks = diff_named_ref_targets(
from_repo.view().local_bookmarks(),
to_repo.view().local_bookmarks(),
)
.collect_vec();
if !changed_local_branches.is_empty() {
if !changed_local_bookmarks.is_empty() {
writeln!(formatter)?;
writeln!(formatter, "Changed local branches:")?;
for (name, (from_target, to_target)) in changed_local_branches {
for (name, (from_target, to_target)) in changed_local_bookmarks {
writeln!(formatter, "{}:", name)?;
write_ref_target_summary(
formatter,
@ -357,8 +357,8 @@ pub fn show_op_diff(
}
let changed_remote_branches = diff_named_remote_refs(
from_repo.view().all_remote_branches(),
to_repo.view().all_remote_branches(),
from_repo.view().all_remote_bookmarks(),
to_repo.view().all_remote_bookmarks(),
)
// Skip updates to the local git repo, since they should typically be covered in
// local branches.

View file

@ -96,7 +96,7 @@ fn view_with_desired_portions_restored(
};
jj_lib::op_store::View {
head_ids: repo_source.head_ids.clone(),
local_branches: repo_source.local_branches.clone(),
local_bookmarks: repo_source.local_bookmarks.clone(),
tags: repo_source.tags.clone(),
remote_views: remote_source.remote_views.clone(),
git_refs: current_view.git_refs.clone(),

View file

@ -33,8 +33,7 @@ use crate::ui::Ui;
///
/// * The working copy commit and its (first) parent, and a summary of the
/// changes between them
///
/// * Conflicted branches (see https://martinvonz.github.io/jj/latest/branches/)
/// * Conflicted bookmarks (see https://martinvonz.github.io/jj/latest/bookmarks/)
#[derive(clap::Args, Clone, Debug)]
#[command(visible_alias = "st")]
pub(crate) struct StatusArgs {
@ -143,47 +142,50 @@ pub(crate) fn cmd_status(
writeln!(formatter, "No working copy")?;
}
let conflicted_local_branches = repo
let conflicted_local_bookmarks = repo
.view()
.local_branches()
.local_bookmarks()
.filter(|(_, target)| target.has_conflict())
.map(|(branch_name, _)| branch_name)
.map(|(bookmark_name, _)| bookmark_name)
.collect_vec();
let conflicted_remote_branches = repo
let conflicted_remote_bookmarks = repo
.view()
.all_remote_branches()
.all_remote_bookmarks()
.filter(|(_, remote_ref)| remote_ref.target.has_conflict())
.map(|(full_name, _)| full_name)
.collect_vec();
if !conflicted_local_branches.is_empty() {
if !conflicted_local_bookmarks.is_empty() {
writeln!(
formatter.labeled("conflict"),
"These branches have conflicts:"
"These bookmarks have conflicts:"
)?;
for branch_name in conflicted_local_branches {
for bookmark_name in conflicted_local_bookmarks {
write!(formatter, " ")?;
write!(formatter.labeled("branch"), "{branch_name}")?;
write!(formatter.labeled("bookmark"), "{bookmark_name}")?;
writeln!(formatter)?;
}
writeln!(
formatter,
" Use `jj branch list` to see details. Use `jj branch set <name> -r <rev>` to \
" Use `jj bookmark list` to see details. Use `jj bookmark set <name> -r <rev>` to \
resolve."
)?;
}
if !conflicted_remote_branches.is_empty() {
if !conflicted_remote_bookmarks.is_empty() {
writeln!(
formatter.labeled("conflict"),
"These remote branches have conflicts:"
"These remote bookmarks have conflicts:"
)?;
for (branch_name, remote_name) in conflicted_remote_branches {
for (bookmark_name, remote_name) in conflicted_remote_bookmarks {
write!(formatter, " ")?;
write!(formatter.labeled("branch"), "{branch_name}@{remote_name}")?;
write!(
formatter.labeled("bookmark"),
"{bookmark_name}@{remote_name}"
)?;
writeln!(formatter)?;
}
writeln!(
formatter,
" Use `jj branch list` to see details. Use `jj git fetch` to resolve."
" Use `jj bookmark list` to see details. Use `jj git fetch` to resolve."
)?;
}

View file

@ -450,16 +450,16 @@ impl<'repo> CommitTemplateBuildFnTable<'repo> {
#[derive(Default)]
pub struct CommitKeywordCache<'repo> {
// Build index lazily, and Rc to get away from &self lifetime.
branches_index: OnceCell<Rc<RefNamesIndex>>,
bookmarks_index: OnceCell<Rc<RefNamesIndex>>,
tags_index: OnceCell<Rc<RefNamesIndex>>,
git_refs_index: OnceCell<Rc<RefNamesIndex>>,
is_immutable_fn: OnceCell<Rc<RevsetContainingFn<'repo>>>,
}
impl<'repo> CommitKeywordCache<'repo> {
pub fn branches_index(&self, repo: &dyn Repo) -> &Rc<RefNamesIndex> {
self.branches_index
.get_or_init(|| Rc::new(build_branches_index(repo)))
pub fn bookmarks_index(&self, repo: &dyn Repo) -> &Rc<RefNamesIndex> {
self.bookmarks_index
.get_or_init(|| Rc::new(build_bookmarks_index(repo)))
}
pub fn tags_index(&self, repo: &dyn Repo) -> &Rc<RefNamesIndex> {
@ -569,10 +569,13 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
},
);
map.insert(
"branches",
"bookmarks",
|language, _build_ctx, self_property, function| {
function.expect_no_arguments()?;
let index = language.keyword_cache.branches_index(language.repo).clone();
let index = language
.keyword_cache
.bookmarks_index(language.repo)
.clone();
let out_property = self_property.map(move |commit| {
index
.get(commit.id())
@ -585,10 +588,13 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
},
);
map.insert(
"local_branches",
"local_bookmarks",
|language, _build_ctx, self_property, function| {
function.expect_no_arguments()?;
let index = language.keyword_cache.branches_index(language.repo).clone();
let index = language
.keyword_cache
.bookmarks_index(language.repo)
.clone();
let out_property = self_property.map(move |commit| {
index
.get(commit.id())
@ -601,10 +607,13 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
},
);
map.insert(
"remote_branches",
"remote_bookmarks",
|language, _build_ctx, self_property, function| {
function.expect_no_arguments()?;
let index = language.keyword_cache.branches_index(language.repo).clone();
let index = language
.keyword_cache
.bookmarks_index(language.repo)
.clone();
let out_property = self_property.map(move |commit| {
index
.get(commit.id())
@ -616,6 +625,11 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
Ok(L::wrap_ref_name_list(out_property))
},
);
// TODO: Remove the following block after jj 0.28+
map.insert("branches", map["bookmarks"]);
map.insert("local_branches", map["local_bookmarks"]);
map.insert("remote_branches", map["remote_bookmarks"]);
map.insert("tags", |language, _build_ctx, self_property, function| {
function.expect_no_arguments()?;
let index = language.keyword_cache.tags_index(language.repo).clone();
@ -1115,14 +1129,14 @@ impl RefNamesIndex {
}
}
fn build_branches_index(repo: &dyn Repo) -> RefNamesIndex {
fn build_bookmarks_index(repo: &dyn Repo) -> RefNamesIndex {
let mut index = RefNamesIndex::default();
for (branch_name, branch_target) in repo.view().branches() {
let local_target = branch_target.local_target;
let remote_refs = branch_target.remote_refs;
for (bookmark_name, bookmark_target) in repo.view().bookmarks() {
let local_target = bookmark_target.local_target;
let remote_refs = bookmark_target.remote_refs;
if local_target.is_present() {
let ref_name = RefName::local(
branch_name,
bookmark_name,
local_target.clone(),
remote_refs.iter().map(|&(_, remote_ref)| remote_ref),
);
@ -1130,7 +1144,7 @@ fn build_branches_index(repo: &dyn Repo) -> RefNamesIndex {
}
for &(remote_name, remote_ref) in &remote_refs {
let ref_name =
RefName::remote(branch_name, remote_name, remote_ref.clone(), local_target);
RefName::remote(bookmark_name, remote_name, remote_ref.clone(), local_target);
index.insert(remote_ref.target.added_ids(), ref_name);
}
}

View file

@ -25,6 +25,11 @@
"username" = "yellow"
"timestamp" = "cyan"
"working_copies" = "green"
"bookmark" = "magenta"
"bookmarks" = "magenta"
"local_bookmarks" = "magenta"
"remote_bookmarks" = "magenta"
# TODO: Remove in jj 0.28+
"branch" = "magenta"
"branches" = "magenta"
"local_branches" = "magenta"

View file

@ -1,18 +1,18 @@
[templates]
branch_list = '''
bookmark_list = '''
if(remote,
if(tracked,
" " ++ separate(" ",
label("branch", "@" ++ remote),
label("bookmark", "@" ++ remote),
format_tracked_remote_ref_distances(self),
) ++ format_ref_targets(self),
label("branch", name ++ "@" ++ remote) ++ format_ref_targets(self),
label("bookmark", name ++ "@" ++ remote) ++ format_ref_targets(self),
),
label("branch", name) ++ if(present, format_ref_targets(self), " (deleted)"),
label("bookmark", name) ++ if(present, format_ref_targets(self), " (deleted)"),
) ++ "\n"
'''
commit_summary = 'format_commit_summary_with_refs(self, branches)'
commit_summary = 'format_commit_summary_with_refs(self, bookmarks)'
config_list = '''
if(overridden,
@ -51,7 +51,7 @@ if(root,
format_short_change_id_with_hidden_and_divergent_info(self),
if(author.email(), author.username(), email_placeholder),
format_timestamp(committer.timestamp()),
branches,
bookmarks,
tags,
working_copies,
git_head,
@ -76,7 +76,7 @@ if(root,
format_short_change_id_with_hidden_and_divergent_info(self),
format_short_signature(author),
format_timestamp(committer.timestamp()),
branches,
bookmarks,
tags,
working_copies,
git_head,
@ -99,7 +99,7 @@ builtin_log_detailed = '''
concat(
"Commit ID: " ++ commit_id ++ "\n",
"Change ID: " ++ change_id ++ "\n",
surround("Branches: ", "\n", separate(" ", local_branches, remote_branches)),
surround("Bookmarks: ", "\n", separate(" ", local_bookmarks, remote_bookmarks)),
surround("Tags: ", "\n", tags),
"Author: " ++ format_detailed_signature(author) ++ "\n",
"Committer: " ++ format_detailed_signature(committer) ++ "\n",
@ -163,7 +163,7 @@ separate(" ",
format_short_change_id(root.change_id()),
label("root", "root()"),
format_short_commit_id(root.commit_id()),
root.branches()
root.bookmarks()
) ++ "\n"
'''

View file

@ -322,7 +322,11 @@ impl RefStatus {
RefName::RemoteBranch { branch, remote } => (
format!("{branch}@{remote}"),
RefKind::Branch,
if repo.view().get_remote_branch(branch, remote).is_tracking() {
if repo
.view()
.get_remote_bookmark(branch, remote)
.is_tracking()
{
TrackingStatus::Tracked
} else {
TrackingStatus::Untracked

View file

@ -13,16 +13,16 @@ This document contains the help content for the `jj` command-line program.
* [`jj`↴](#jj)
* [`jj abandon`↴](#jj-abandon)
* [`jj backout`↴](#jj-backout)
* [`jj branch`↴](#jj-branch)
* [`jj branch create`↴](#jj-branch-create)
* [`jj branch delete`↴](#jj-branch-delete)
* [`jj branch forget`↴](#jj-branch-forget)
* [`jj branch list`↴](#jj-branch-list)
* [`jj branch move`↴](#jj-branch-move)
* [`jj branch rename`↴](#jj-branch-rename)
* [`jj branch set`↴](#jj-branch-set)
* [`jj branch track`↴](#jj-branch-track)
* [`jj branch untrack`↴](#jj-branch-untrack)
* [`jj bookmark`↴](#jj-bookmark)
* [`jj bookmark create`↴](#jj-bookmark-create)
* [`jj bookmark delete`↴](#jj-bookmark-delete)
* [`jj bookmark forget`↴](#jj-bookmark-forget)
* [`jj bookmark list`↴](#jj-bookmark-list)
* [`jj bookmark move`↴](#jj-bookmark-move)
* [`jj bookmark rename`↴](#jj-bookmark-rename)
* [`jj bookmark set`↴](#jj-bookmark-set)
* [`jj bookmark track`↴](#jj-bookmark-track)
* [`jj bookmark untrack`↴](#jj-bookmark-untrack)
* [`jj commit`↴](#jj-commit)
* [`jj config`↴](#jj-config)
* [`jj config edit`↴](#jj-config-edit)
@ -113,7 +113,7 @@ To get started, see the tutorial at https://martinvonz.github.io/jj/latest/tutor
* `abandon` — Abandon a revision
* `backout` — Apply the reverse of a revision on top of another revision
* `branch` — Manage branches
* `bookmark` — Manage bookmarks
* `commit` — Update the description and create a new change on top
* `config` — Manage config options
* `describe` — Update the change description or other metadata
@ -226,201 +226,201 @@ Apply the reverse of a revision on top of another revision
## `jj branch`
## `jj bookmark`
Manage branches
Manage bookmarks
For information about branches, see https://martinvonz.github.io/jj/latest/branches/.
For information about bookmarks, see https://martinvonz.github.io/jj/latest/docs/bookmarks.md.
**Usage:** `jj branch <COMMAND>`
**Usage:** `jj bookmark <COMMAND>`
###### **Subcommands:**
* `create` — Create a new branch
* `delete` — Delete an existing branch and propagate the deletion to remotes on the next push
* `forget` — Forget everything about a branch, including its local and remote targets
* `list` — List branches and their targets
* `move` — Move existing branches to target revision
* `rename` — Rename `old` branch name to `new` branch name
* `set` — Create or update a branch to point to a certain commit
* `track` — Start tracking given remote branches
* `untrack` — Stop tracking given remote branches
* `create` — Create a new bookmark
* `delete` — Delete an existing bookmark and propagate the deletion to remotes on the next push
* `forget` — Forget everything about a bookmark, including its local and remote targets
* `list` — List bookmarks and their targets
* `move` — Move existing bookmarks to target revision
* `rename` — Rename `old` bookmark name to `new` bookmark name
* `set` — Create or update a bookmark to point to a certain commit
* `track` — Start tracking given remote bookmarks
* `untrack` — Stop tracking given remote bookmarks
## `jj branch create`
## `jj bookmark create`
Create a new branch
Create a new bookmark
**Usage:** `jj branch create [OPTIONS] <NAMES>...`
**Usage:** `jj bookmark create [OPTIONS] <NAMES>...`
###### **Arguments:**
* `<NAMES>` — The branches to create
* `<NAMES>` — The bookmarks to create
###### **Options:**
* `-r`, `--revision <REVISION>` — The branch's target revision
* `-r`, `--revision <REVISION>` — The bookmark's target revision
## `jj branch delete`
## `jj bookmark delete`
Delete an existing branch and propagate the deletion to remotes on the next push
Delete an existing bookmark and propagate the deletion to remotes on the next push
**Usage:** `jj branch delete <NAMES>...`
**Usage:** `jj bookmark delete <NAMES>...`
###### **Arguments:**
* `<NAMES>` — The branches to delete
* `<NAMES>` — The bookmarks to delete
By default, the specified name matches exactly. Use `glob:` prefix to select branches by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
By default, the specified name matches exactly. Use `glob:` prefix to select bookmarks by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
## `jj branch forget`
## `jj bookmark forget`
Forget everything about a branch, including its local and remote targets
Forget everything about a bookmark, including its local and remote targets
A forgotten branch will not impact remotes on future pushes. It will be recreated on future pulls if it still exists in the remote.
A forgotten bookmark will not impact remotes on future pushes. It will be recreated on future pulls if it still exists in the remote.
**Usage:** `jj branch forget <NAMES>...`
**Usage:** `jj bookmark forget <NAMES>...`
###### **Arguments:**
* `<NAMES>` — The branches to forget
* `<NAMES>` — The bookmarks to forget
By default, the specified name matches exactly. Use `glob:` prefix to select branches by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
By default, the specified name matches exactly. Use `glob:` prefix to select bookmarks by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
## `jj branch list`
## `jj bookmark list`
List branches and their targets
List bookmarks and their targets
By default, a tracking remote branch will be included only if its target is different from the local target. A non-tracking remote branch won't be listed. For a conflicted branch (both local and remote), old target revisions are preceded by a "-" and new target revisions are preceded by a "+".
By default, a tracking remote bookmark will be included only if its target is different from the local target. A non-tracking remote bookmark won't be listed. For a conflicted bookmark (both local and remote), old target revisions are preceded by a "-" and new target revisions are preceded by a "+".
For information about branches, see https://martinvonz.github.io/jj/latest/branches/.
For information about bookmarks, see https://martinvonz.github.io/jj/docs/bookmarks.md.
**Usage:** `jj branch list [OPTIONS] [NAMES]...`
**Usage:** `jj bookmark list [OPTIONS] [NAMES]...`
###### **Arguments:**
* `<NAMES>` — Show branches whose local name matches
* `<NAMES>` — Show bookmarks whose local name matches
By default, the specified name matches exactly. Use `glob:` prefix to select branches by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
By default, the specified name matches exactly. Use `glob:` prefix to select bookmarks by wildcard pattern. For details, see https://martinvonz.github.io/jj/docs/revsets.md#string-patterns.
###### **Options:**
* `-a`, `--all-remotes` — Show all tracking and non-tracking remote branches including the ones whose targets are synchronized with the local branches
* `-t`, `--tracked` — Show remote tracked branches only. Omits local Git-tracking branches by default
* `-c`, `--conflicted` — Show conflicted branches only
* `-r`, `--revisions <REVISIONS>` — Show branches whose local targets are in the given revisions
* `-a`, `--all-remotes` — Show all tracking and non-tracking remote bookmarks including the ones whose targets are synchronized with the local bookmarks
* `-t`, `--tracked` — Show remote tracked bookmarks only. Omits local Git-tracking bookmarks by default
* `-c`, `--conflicted` — Show conflicted bookmarks only
* `-r`, `--revisions <REVISIONS>` — Show bookmarks whose local targets are in the given revisions
Note that `-r deleted_branch` will not work since `deleted_branch` wouldn't have a local target.
* `-T`, `--template <TEMPLATE>` — Render each branch using the given template
Note that `-r deleted_bookmark` will not work since `deleted_bookmark` wouldn't have a local target.
* `-T`, `--template <TEMPLATE>` — Render each bookmark using the given template
All 0-argument methods of the `RefName` type are available as keywords.
For the syntax, see https://martinvonz.github.io/jj/latest/templates/
For the syntax, see https://martinvonz.github.io/jj/latest/docs/templates.md
## `jj branch move`
## `jj bookmark move`
Move existing branches to target revision
Move existing bookmarks to target revision
If branch names are given, the specified branches will be updated to point to the target revision.
If bookmark names are given, the specified bookmarks will be updated to point to the target revision.
If `--from` options are given, branches currently pointing to the specified revisions will be updated. The branches can also be filtered by names.
If `--from` options are given, bookmarks currently pointing to the specified revisions will be updated. The bookmarks can also be filtered by names.
Example: pull up the nearest branches to the working-copy parent
Example: pull up the nearest bookmarks to the working-copy parent
$ jj branch move --from 'heads(::@- & branches())' --to @-
$ jj bookmark move --from 'heads(::@- & bookmarks())' --to @-
**Usage:** `jj branch move [OPTIONS] <--from <REVISIONS>|NAMES>`
**Usage:** `jj bookmark move [OPTIONS] <--from <REVISIONS>|NAMES>`
###### **Arguments:**
* `<NAMES>` — Move branches matching the given name patterns
* `<NAMES>` — Move bookmarks matching the given name patterns
By default, the specified name matches exactly. Use `glob:` prefix to select branches by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
By default, the specified name matches exactly. Use `glob:` prefix to select bookmarks by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
###### **Options:**
* `--from <REVISIONS>` — Move branches from the given revisions
* `--to <REVISION>` — Move branches to this revision
* `--from <REVISIONS>` — Move bookmarks from the given revisions
* `--to <REVISION>` — Move bookmarks to this revision
Default value: `@`
* `-B`, `--allow-backwards` — Allow moving branches backwards or sideways
* `-B`, `--allow-backwards` — Allow moving bookmarks backwards or sideways
## `jj branch rename`
## `jj bookmark rename`
Rename `old` branch name to `new` branch name
Rename `old` bookmark name to `new` bookmark name
The new branch name points at the same commit as the old branch name.
The new bookmark name points at the same commit as the old bookmark name.
**Usage:** `jj branch rename <OLD> <NEW>`
**Usage:** `jj bookmark rename <OLD> <NEW>`
###### **Arguments:**
* `<OLD>` — The old name of the branch
* `<NEW>` — The new name of the branch
* `<OLD>` — The old name of the bookmark
* `<NEW>` — The new name of the bookmark
## `jj branch set`
## `jj bookmark set`
Create or update a branch to point to a certain commit
Create or update a bookmark to point to a certain commit
**Usage:** `jj branch set [OPTIONS] <NAMES>...`
**Usage:** `jj bookmark set [OPTIONS] <NAMES>...`
###### **Arguments:**
* `<NAMES>` — The branches to update
* `<NAMES>` — The bookmarks to update
###### **Options:**
* `-r`, `--revision <REVISION>` — The branch's target revision
* `-B`, `--allow-backwards` — Allow moving the branch backwards or sideways
* `-r`, `--revision <REVISION>` — The bookmark's target revision
* `-B`, `--allow-backwards` — Allow moving the bookmark backwards or sideways
## `jj branch track`
## `jj bookmark track`
Start tracking given remote branches
Start tracking given remote bookmarks
A tracking remote branch will be imported as a local branch of the same name. Changes to it will propagate to the existing local branch on future pulls.
A tracking remote bookmark will be imported as a local bookmark of the same name. Changes to it will propagate to the existing local bookmark on future pulls.
**Usage:** `jj branch track <BRANCH@REMOTE>...`
**Usage:** `jj bookmark track <BRANCH@REMOTE>...`
###### **Arguments:**
* `<BRANCH@REMOTE>` — Remote branches to track
* `<BRANCH@REMOTE>` — Remote bookmarks to track
By default, the specified name matches exactly. Use `glob:` prefix to select branches by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
By default, the specified name matches exactly. Use `glob:` prefix to select bookmarks by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
Examples: branch@remote, glob:main@*, glob:jjfan-*@upstream
Examples: bookmark@remote, glob:main@*, glob:jjfan-*@upstream
## `jj branch untrack`
## `jj bookmark untrack`
Stop tracking given remote branches
Stop tracking given remote bookmarks
A non-tracking remote branch is just a pointer to the last-fetched remote branch. It won't be imported as a local branch on future pulls.
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.
**Usage:** `jj branch untrack <BRANCH@REMOTE>...`
**Usage:** `jj bookmark untrack <BRANCH@REMOTE>...`
###### **Arguments:**
* `<BRANCH@REMOTE>` — Remote branches to untrack
* `<BRANCH@REMOTE>` — Remote bookmarks to untrack
By default, the specified name matches exactly. Use `glob:` prefix to select branches by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
By default, the specified name matches exactly. Use `glob:` prefix to select bookmarks by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets/#string-patterns.
Examples: branch@remote, glob:main@*, glob:jjfan-*@upstream
Examples: bookmark@remote, glob:main@*, glob:jjfan-*@upstream
@ -1032,33 +1032,33 @@ Create a new Git backed repo
Push to a Git remote
By default, pushes any branches pointing to `remote_branches(remote=<remote>)..@`. Use `--branch` to push specific branches. Use `--all` to push all branches. Use `--change` to generate branch names based on the change IDs of specific commits.
By default, pushes any bookmarks pointing to `remote_bookmarks(remote=<remote>)..@`. Use `--bookmark` to push specific bookmarks. Use `--all` to push all bookmarks. Use `--change` to generate bookmark names based on the change IDs of specific commits.
Before the command actually moves, creates, or deletes a remote branch, it makes several [safety checks]. If there is a problem, you may need to run `jj git fetch --remote <remote name>` and/or resolve some [branch conflicts].
Before the command actually moves, creates, or deletes a remote bookmark, it makes several [safety checks]. If there is a problem, you may need to run `jj git fetch --remote <remote name>` and/or resolve some [bookmark conflicts].
[safety checks]: https://martinvonz.github.io/jj/latest/branches/#pushing-branches-safety-checks
[safety checks]: https://martinvonz.github.io/jj/latest/bookmarks/#pushing-bookmarks-safety-checks
[branch conflicts]: https://martinvonz.github.io/jj/latest/branches/#conflicts
[bookmark conflicts]: https://martinvonz.github.io/jj/latest/bookmarks/#conflicts
**Usage:** `jj git push [OPTIONS]`
###### **Options:**
* `--remote <REMOTE>` — The remote to push to (only named remotes are supported)
* `-b`, `--branch <BRANCH>` — Push only this branch, or branches matching a pattern (can be repeated)
* `-b`, `--bookmark <BOOKMARK>` — Push only this bookmark, or bookmarks matching a pattern (can be repeated)
By default, the specified name matches exactly. Use `glob:` prefix to select branches by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets#string-patterns.
* `--all` — Push all branches (including deleted branches)
* `--tracked` — Push all tracked branches (including deleted branches)
By default, the specified name matches exactly. Use `glob:` prefix to select bookmarks by wildcard pattern. For details, see https://martinvonz.github.io/jj/latest/revsets#string-patterns.
* `--all` — Push all bookmarks (including deleted bookmarks)
* `--tracked` — Push all tracked bookmarks (including deleted bookmarks)
This usually means that the branch was already pushed to or fetched from the relevant remote. For details, see https://martinvonz.github.io/jj/latest/branches#remotes-and-tracked-branches
* `--deleted` — Push all deleted branches
This usually means that the bookmark was already pushed to or fetched from the relevant remote. For details, see https://martinvonz.github.io/jj/latest/bookmarks#remotes-and-tracked-bookmarks
* `--deleted` — Push all deleted bookmarks
Only tracked branches can be successfully deleted on the remote. A warning will be printed if any untracked branches on the remote correspond to missing local branches.
Only tracked bookmarks can be successfully deleted on the remote. A warning will be printed if any untracked bookmarks on the remote correspond to missing local bookmarks.
* `--allow-empty-description` — Allow pushing commits with empty descriptions
* `--allow-private` — Allow pushing commits that are private
* `-r`, `--revisions <REVISIONS>` — Push branches pointing to these commits (can be repeated)
* `-c`, `--change <CHANGE>` — Push this commit by creating a branch based on its change ID (can be repeated)
* `-r`, `--revisions <REVISIONS>` — Push bookmarks pointing to these commits (can be repeated)
* `-c`, `--change <CHANGE>` — Push this commit by creating a bookmark based on its change ID (can be repeated)
* `--dry-run` — Only display what will change on the remote
@ -1916,9 +1916,7 @@ Show high-level repo status
This includes:
* The working copy commit and its (first) parent, and a summary of the changes between them
* Conflicted branches (see https://martinvonz.github.io/jj/latest/branches/)
* The working copy commit and its (first) parent, and a summary of the changes between them * Conflicted bookmarks (see https://martinvonz.github.io/jj/latest/bookmarks/)
**Usage:** `jj status [PATHS]...`

View file

@ -10,10 +10,10 @@ fn test_no_forgotten_test_files() {
mod test_abandon_command;
mod test_acls;
mod test_advance_branches;
mod test_advance_bookmarks;
mod test_alias;
mod test_backout_command;
mod test_branch_command;
mod test_bookmark_command;
mod test_builtin_aliases;
mod test_checkout;
mod test_commit_command;

View file

@ -25,7 +25,7 @@ fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, paren
test_env.jj_cmd_ok(repo_path, &args);
}
std::fs::write(repo_path.join(name), format!("{name}\n")).unwrap();
test_env.jj_cmd_ok(repo_path, &["branch", "create", name]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", name]);
}
#[test]
@ -291,7 +291,7 @@ fn test_bug_2600() {
[rlv] nottherootcommit
[zzz]
"###);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "list", "b"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["bookmark", "list", "b"]);
insta::assert_snapshot!(stdout, @r###"
b: zsuskuln 73c929fc base
"###);
@ -370,7 +370,7 @@ fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
&[
"log",
"-T",
r#"separate(" ", "[" ++ change_id.short(3) ++ "]", branches)"#,
r#"separate(" ", "[" ++ change_id.short(3) ++ "]", bookmarks)"#,
],
)
}

View file

@ -0,0 +1,455 @@
// Copyright 2024 The Jujutsu Authors
//
// 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 std::path::Path;
use test_case::test_case;
use crate::common::TestEnvironment;
fn get_log_output_with_bookmarks(test_env: &TestEnvironment, cwd: &Path) -> String {
// Don't include commit IDs since they will be different depending on
// whether the test runs with `jj commit` or `jj describe` + `jj new`.
let template = r#""bookmarks{" ++ local_bookmarks ++ "} desc: " ++ description"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}
fn set_advance_bookmarks(test_env: &TestEnvironment, enabled: bool) {
if enabled {
test_env.add_config(
r#"[experimental-advance-branches]
enabled-branches = ["glob:*"]
"#,
);
} else {
test_env.add_config(
r#"[experimental-advance-branches]
enabled-branches = []
"#,
);
}
}
// Runs a command in the specified test environment and workspace path that
// describes the current commit with `commit_message` and creates a new commit
// on top of it.
type CommitFn = fn(env: &TestEnvironment, workspace_path: &Path, commit_message: &str);
// Implements CommitFn using the `jj commit` command.
fn commit_cmd(env: &TestEnvironment, workspace_path: &Path, commit_message: &str) {
env.jj_cmd_ok(workspace_path, &["commit", "-m", commit_message]);
}
// Implements CommitFn using the `jj describe` and `jj new`.
fn describe_new_cmd(env: &TestEnvironment, workspace_path: &Path, commit_message: &str) {
env.jj_cmd_ok(workspace_path, &["describe", "-m", commit_message]);
env.jj_cmd_ok(workspace_path, &["new"]);
}
// Check that enabling and disabling advance-bookmarks works as expected.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_bookmarks_enabled(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
// First, test with advance-bookmarks enabled. Start by creating a bookmark on
// the root commit.
set_advance_bookmarks(&test_env, true);
test_env.jj_cmd_ok(
&workspace_path,
&["bookmark", "create", "-r", "@-", "test_bookmark"],
);
// Check the initial state of the repo.
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{test_bookmark} desc:
"###);
}
// Run jj commit, which will advance the bookmark pointing to @-.
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{test_bookmark} desc: first
bookmarks{} desc:
"###);
}
// Now disable advance bookmarks and commit again. The bookmark shouldn't move.
set_advance_bookmarks(&test_env, false);
make_commit(&test_env, &workspace_path, "second");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc: second
bookmarks{test_bookmark} desc: first
bookmarks{} desc:
"###);
}
}
// Check that only a bookmark pointing to @- advances. Branches pointing to @
// are not advanced.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_bookmarks_at_minus(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_bookmarks(&test_env, true);
test_env.jj_cmd_ok(&workspace_path, &["bookmark", "create", "test_bookmark"]);
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{test_bookmark} desc:
bookmarks{} desc:
"###);
}
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{test_bookmark} desc: first
bookmarks{} desc:
"###);
}
// Create a second bookmark pointing to @. On the next commit, only the first
// bookmark, which points to @-, will advance.
test_env.jj_cmd_ok(&workspace_path, &["bookmark", "create", "test_bookmark2"]);
make_commit(&test_env, &workspace_path, "second");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{test_bookmark test_bookmark2} desc: second
bookmarks{} desc: first
bookmarks{} desc:
"###);
}
}
// Test that per-bookmark overrides invert the behavior of
// experimental-advance-bookmarks.enabled.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_bookmarks_overrides(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
// advance-bookmarks is disabled by default.
test_env.jj_cmd_ok(
&workspace_path,
&["bookmark", "create", "-r", "@-", "test_bookmark"],
);
// Check the initial state of the repo.
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{test_bookmark} desc:
"###);
}
// Commit will not advance the bookmark since advance-bookmarks is disabled.
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc: first
bookmarks{test_bookmark} desc:
"###);
}
// Now enable advance bookmarks for "test_bookmark", move the bookmark, and
// commit again.
test_env.add_config(
r#"[experimental-advance-bookmarks]
enabled-bookmarks = ["test_bookmark"]
"#,
);
test_env.jj_cmd_ok(
&workspace_path,
&["bookmark", "set", "test_bookmark", "-r", "@-"],
);
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{test_bookmark} desc: first
bookmarks{} desc:
"###);
}
make_commit(&test_env, &workspace_path, "second");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc: second
bookmarks{test_bookmark} desc: first
bookmarks{} desc:
"###);
}
// Now disable advance bookmarks for "test_bookmark" and "second_bookmark",
// which we will use later. Disabling always takes precedence over enabling.
test_env.add_config(
r#"[experimental-advance-bookmarks]
enabled-bookmarks = ["test_bookmark", "second_bookmark"]
disabled-bookmarks = ["test_bookmark"]
"#,
);
make_commit(&test_env, &workspace_path, "third");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc: third
bookmarks{} desc: second
bookmarks{test_bookmark} desc: first
bookmarks{} desc:
"###);
}
// If we create a new bookmark at @- and move test_bookmark there as well. When
// we commit, only "second_bookmark" will advance since "test_bookmark" is
// disabled.
test_env.jj_cmd_ok(
&workspace_path,
&["bookmark", "create", "second_bookmark", "-r", "@-"],
);
test_env.jj_cmd_ok(
&workspace_path,
&["bookmark", "set", "test_bookmark", "-r", "@-"],
);
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{second_bookmark test_bookmark} desc: third
bookmarks{} desc: second
bookmarks{} desc: first
bookmarks{} desc:
"###);
}
make_commit(&test_env, &workspace_path, "fourth");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc: fourth
bookmarks{second_bookmark test_bookmark} desc: third
bookmarks{} desc: second
bookmarks{} desc: first
bookmarks{} desc:
"###);
}
}
// If multiple eligible bookmarks point to @-, all of them will be advanced.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_bookmarks_multiple_bookmarks(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_bookmarks(&test_env, true);
test_env.jj_cmd_ok(
&workspace_path,
&["bookmark", "create", "-r", "@-", "first_bookmark"],
);
test_env.jj_cmd_ok(
&workspace_path,
&["bookmark", "create", "-r", "@-", "second_bookmark"],
);
insta::allow_duplicates! {
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{first_bookmark second_bookmark} desc:
"###);
}
// Both bookmarks are eligible and both will advance.
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{first_bookmark second_bookmark} desc: first
bookmarks{} desc:
"###);
}
}
// Call `jj new` on an interior commit and see that the bookmark pointing to its
// parent's parent is advanced.
#[test]
fn test_new_advance_bookmarks_interior() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_bookmarks(&test_env, true);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc:
"###);
// Create a gap in the commits for us to insert our new commit with --before.
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "first"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "second"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "third"]);
test_env.jj_cmd_ok(
&workspace_path,
&["bookmark", "create", "-r", "@---", "test_bookmark"],
);
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc: third
bookmarks{} desc: second
bookmarks{test_bookmark} desc: first
bookmarks{} desc:
"###);
test_env.jj_cmd_ok(&workspace_path, &["new", "-r", "@--"]);
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc: third
bookmarks{test_bookmark} desc: second
bookmarks{} desc: first
bookmarks{} desc:
"###);
}
// If the `--before` flag is passed to `jj new`, bookmarks are not advanced.
#[test]
fn test_new_advance_bookmarks_before() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_bookmarks(&test_env, true);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc:
"###);
// Create a gap in the commits for us to insert our new commit with --before.
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "first"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "second"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "third"]);
test_env.jj_cmd_ok(
&workspace_path,
&["bookmark", "create", "-r", "@---", "test_bookmark"],
);
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc: third
bookmarks{} desc: second
bookmarks{test_bookmark} desc: first
bookmarks{} desc:
"###);
test_env.jj_cmd_ok(&workspace_path, &["new", "--before", "@-"]);
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
bookmarks{} desc: third
@ bookmarks{} desc:
bookmarks{} desc: second
bookmarks{test_bookmark} desc: first
bookmarks{} desc:
"###);
}
// If the `--after` flag is passed to `jj new`, bookmarks are not advanced.
#[test]
fn test_new_advance_bookmarks_after() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_bookmarks(&test_env, true);
test_env.jj_cmd_ok(
&workspace_path,
&["bookmark", "create", "-r", "@-", "test_bookmark"],
);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{test_bookmark} desc:
"###);
test_env.jj_cmd_ok(&workspace_path, &["describe", "-m", "first"]);
test_env.jj_cmd_ok(&workspace_path, &["new", "--after", "@"]);
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc: first
bookmarks{test_bookmark} desc:
"###);
}
#[test]
fn test_new_advance_bookmarks_merge_children() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_bookmarks(&test_env, true);
test_env.jj_cmd_ok(&workspace_path, &["desc", "-m", "0"]);
test_env.jj_cmd_ok(&workspace_path, &["new", "-m", "1"]);
test_env.jj_cmd_ok(&workspace_path, &["new", "description(0)", "-m", "2"]);
test_env.jj_cmd_ok(
&workspace_path,
&[
"bookmark",
"create",
"test_bookmark",
"-r",
"description(0)",
],
);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc: 2
bookmarks{} desc: 1
bookmarks{test_bookmark} desc: 0
bookmarks{} desc:
"###);
// The bookmark won't advance because `jj new` had multiple targets.
test_env.jj_cmd_ok(
&workspace_path,
&["new", "description(1)", "description(2)"],
);
insta::assert_snapshot!(get_log_output_with_bookmarks(&test_env, &workspace_path), @r###"
@ bookmarks{} desc:
bookmarks{} desc: 2
bookmarks{} desc: 1
bookmarks{test_bookmark} desc: 0
bookmarks{} desc:
"###);
}

View file

@ -1,448 +0,0 @@
// Copyright 2024 The Jujutsu Authors
//
// 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 std::path::Path;
use test_case::test_case;
use crate::common::TestEnvironment;
fn get_log_output_with_branches(test_env: &TestEnvironment, cwd: &Path) -> String {
// Don't include commit IDs since they will be different depending on
// whether the test runs with `jj commit` or `jj describe` + `jj new`.
let template = r#""branches{" ++ local_branches ++ "} desc: " ++ description"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}
fn set_advance_branches(test_env: &TestEnvironment, enabled: bool) {
if enabled {
test_env.add_config(
r#"[experimental-advance-branches]
enabled-branches = ["glob:*"]
"#,
);
} else {
test_env.add_config(
r#"[experimental-advance-branches]
enabled-branches = []
"#,
);
}
}
// Runs a command in the specified test environment and workspace path that
// describes the current commit with `commit_message` and creates a new commit
// on top of it.
type CommitFn = fn(env: &TestEnvironment, workspace_path: &Path, commit_message: &str);
// Implements CommitFn using the `jj commit` command.
fn commit_cmd(env: &TestEnvironment, workspace_path: &Path, commit_message: &str) {
env.jj_cmd_ok(workspace_path, &["commit", "-m", commit_message]);
}
// Implements CommitFn using the `jj describe` and `jj new`.
fn describe_new_cmd(env: &TestEnvironment, workspace_path: &Path, commit_message: &str) {
env.jj_cmd_ok(workspace_path, &["describe", "-m", commit_message]);
env.jj_cmd_ok(workspace_path, &["new"]);
}
// Check that enabling and disabling advance-branches works as expected.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_branches_enabled(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
// First, test with advance-branches enabled. Start by creating a branch on the
// root commit.
set_advance_branches(&test_env, true);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@-", "test_branch"],
);
// Check the initial state of the repo.
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{test_branch} desc:
"###);
}
// Run jj commit, which will advance the branch pointing to @-.
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{test_branch} desc: first
branches{} desc:
"###);
}
// Now disable advance branches and commit again. The branch shouldn't move.
set_advance_branches(&test_env, false);
make_commit(&test_env, &workspace_path, "second");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{} desc: second
branches{test_branch} desc: first
branches{} desc:
"###);
}
}
// Check that only a branch pointing to @- advances. Branches pointing to @ are
// not advanced.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_branches_at_minus(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
test_env.jj_cmd_ok(&workspace_path, &["branch", "create", "test_branch"]);
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{test_branch} desc:
branches{} desc:
"###);
}
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{test_branch} desc: first
branches{} desc:
"###);
}
// Create a second branch pointing to @. On the next commit, only the first
// branch, which points to @-, will advance.
test_env.jj_cmd_ok(&workspace_path, &["branch", "create", "test_branch2"]);
make_commit(&test_env, &workspace_path, "second");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{test_branch test_branch2} desc: second
branches{} desc: first
branches{} desc:
"###);
}
}
// Test that per-branch overrides invert the behavior of
// experimental-advance-branches.enabled.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_branches_overrides(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
// advance-branches is disabled by default.
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@-", "test_branch"],
);
// Check the initial state of the repo.
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{test_branch} desc:
"###);
}
// Commit will not advance the branch since advance-branches is disabled.
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{} desc: first
branches{test_branch} desc:
"###);
}
// Now enable advance branches for "test_branch", move the branch, and commit
// again.
test_env.add_config(
r#"[experimental-advance-branches]
enabled-branches = ["test_branch"]
"#,
);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "set", "test_branch", "-r", "@-"],
);
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{test_branch} desc: first
branches{} desc:
"###);
}
make_commit(&test_env, &workspace_path, "second");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{test_branch} desc: second
branches{} desc: first
branches{} desc:
"###);
}
// Now disable advance branches for "test_branch" and "second_branch", which
// we will use later. Disabling always takes precedence over enabling.
test_env.add_config(
r#"[experimental-advance-branches]
enabled-branches = ["test_branch", "second_branch"]
disabled-branches = ["test_branch"]
"#,
);
make_commit(&test_env, &workspace_path, "third");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{} desc: third
branches{test_branch} desc: second
branches{} desc: first
branches{} desc:
"###);
}
// If we create a new branch at @- and move test_branch there as well. When
// we commit, only "second_branch" will advance since "test_branch" is disabled.
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "second_branch", "-r", "@-"],
);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "set", "test_branch", "-r", "@-"],
);
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{second_branch test_branch} desc: third
branches{} desc: second
branches{} desc: first
branches{} desc:
"###);
}
make_commit(&test_env, &workspace_path, "fourth");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{second_branch} desc: fourth
branches{test_branch} desc: third
branches{} desc: second
branches{} desc: first
branches{} desc:
"###);
}
}
// If multiple eligible branches point to @-, all of them will be advanced.
#[test_case(commit_cmd ; "commit")]
#[test_case(describe_new_cmd; "new")]
fn test_advance_branches_multiple_branches(make_commit: CommitFn) {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@-", "first_branch"],
);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@-", "second_branch"],
);
insta::allow_duplicates! {
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{first_branch second_branch} desc:
"###);
}
// Both branches are eligible and both will advance.
make_commit(&test_env, &workspace_path, "first");
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{first_branch second_branch} desc: first
branches{} desc:
"###);
}
}
// Call `jj new` on an interior commit and see that the branch pointing to its
// parent's parent is advanced.
#[test]
fn test_new_advance_branches_interior() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{} desc:
"###);
// Create a gap in the commits for us to insert our new commit with --before.
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "first"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "second"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "third"]);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@---", "test_branch"],
);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{} desc: third
branches{} desc: second
branches{test_branch} desc: first
branches{} desc:
"###);
test_env.jj_cmd_ok(&workspace_path, &["new", "-r", "@--"]);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{} desc: third
branches{test_branch} desc: second
branches{} desc: first
branches{} desc:
"###);
}
// If the `--before` flag is passed to `jj new`, branches are not advanced.
#[test]
fn test_new_advance_branches_before() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{} desc:
"###);
// Create a gap in the commits for us to insert our new commit with --before.
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "first"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "second"]);
test_env.jj_cmd_ok(&workspace_path, &["commit", "-m", "third"]);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@---", "test_branch"],
);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{} desc: third
branches{} desc: second
branches{test_branch} desc: first
branches{} desc:
"###);
test_env.jj_cmd_ok(&workspace_path, &["new", "--before", "@-"]);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
branches{} desc: third
@ branches{} desc:
branches{} desc: second
branches{test_branch} desc: first
branches{} desc:
"###);
}
// If the `--after` flag is passed to `jj new`, branches are not advanced.
#[test]
fn test_new_advance_branches_after() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "-r", "@-", "test_branch"],
);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{test_branch} desc:
"###);
test_env.jj_cmd_ok(&workspace_path, &["describe", "-m", "first"]);
test_env.jj_cmd_ok(&workspace_path, &["new", "--after", "@"]);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{} desc: first
branches{test_branch} desc:
"###);
}
#[test]
fn test_new_advance_branches_merge_children() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let workspace_path = test_env.env_root().join("repo");
set_advance_branches(&test_env, true);
test_env.jj_cmd_ok(&workspace_path, &["desc", "-m", "0"]);
test_env.jj_cmd_ok(&workspace_path, &["new", "-m", "1"]);
test_env.jj_cmd_ok(&workspace_path, &["new", "description(0)", "-m", "2"]);
test_env.jj_cmd_ok(
&workspace_path,
&["branch", "create", "test_branch", "-r", "description(0)"],
);
// Check the initial state of the repo.
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc: 2
branches{} desc: 1
branches{test_branch} desc: 0
branches{} desc:
"###);
// The branch won't advance because `jj new` had multiple targets.
test_env.jj_cmd_ok(
&workspace_path,
&["new", "description(1)", "description(2)"],
);
insta::assert_snapshot!(get_log_output_with_branches(&test_env, &workspace_path), @r###"
@ branches{} desc:
branches{} desc: 2
branches{} desc: 1
branches{test_branch} desc: 0
branches{} desc:
"###);
}

View file

@ -24,11 +24,11 @@ fn test_alias_basic() {
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.add_config(r#"aliases.b = ["log", "-r", "@", "-T", "branches"]"#);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "my-branch"]);
test_env.add_config(r#"aliases.b = ["log", "-r", "@", "-T", "bookmarks"]"#);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "my-bookmark"]);
let stdout = test_env.jj_cmd_success(&repo_path, &["b"]);
insta::assert_snapshot!(stdout, @r###"
@ my-branch
@ my-bookmark
~
"###);
@ -41,17 +41,17 @@ fn test_alias_legacy_section() {
let repo_path = test_env.env_root().join("repo");
// Can define aliases in [alias] section
test_env.add_config(r#"alias.b = ["log", "-r", "@", "-T", "branches"]"#);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "my-branch"]);
test_env.add_config(r#"alias.b = ["log", "-r", "@", "-T", "bookmarks"]"#);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "my-bookmark"]);
let stdout = test_env.jj_cmd_success(&repo_path, &["b"]);
insta::assert_snapshot!(stdout, @r###"
@ my-branch
@ my-bookmark
~
"###);
// The same alias (name) in both [alias] and [aliases] sections is an error
test_env.add_config(r#"aliases.b = ["branch", "list"]"#);
test_env.add_config(r#"aliases.b = ["bookmark", "list"]"#);
let stderr = test_env.jj_cmd_failure(&repo_path, &["b"]);
insta::assert_snapshot!(stderr, @r###"
Error: Alias "b" is defined in both [aliases] and [alias]

View file

@ -33,7 +33,7 @@ fn create_commit(
for (name, contents) in files {
std::fs::write(repo_path.join(name), contents).unwrap();
}
test_env.jj_cmd_ok(repo_path, &["branch", "create", name]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", name]);
}
#[test]

View file

@ -27,9 +27,9 @@ fn set_up(trunk_name: &str) -> (TestEnvironment, PathBuf) {
.join("git");
test_env.jj_cmd_ok(&origin_path, &["describe", "-m=description 1"]);
test_env.jj_cmd_ok(&origin_path, &["branch", "create", trunk_name]);
test_env.jj_cmd_ok(&origin_path, &["bookmark", "create", trunk_name]);
test_env.jj_cmd_ok(&origin_path, &["new", "root()", "-m=description 2"]);
test_env.jj_cmd_ok(&origin_path, &["branch", "create", "unrelated_branch"]);
test_env.jj_cmd_ok(&origin_path, &["bookmark", "create", "unrelated_bookmark"]);
test_env.jj_cmd_ok(&origin_path, &["git", "export"]);
test_env.jj_cmd_ok(
@ -87,7 +87,7 @@ fn test_builtin_alias_trunk_matches_exactly_one_commit() {
let (test_env, workspace_root) = set_up("main");
let origin_path = test_env.env_root().join("origin");
test_env.jj_cmd_ok(&origin_path, &["new", "root()", "-m=description 3"]);
test_env.jj_cmd_ok(&origin_path, &["branch", "create", "master"]);
test_env.jj_cmd_ok(&origin_path, &["bookmark", "create", "master"]);
let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "trunk()"]);
insta::assert_snapshot!(stdout, @r###"
@ -102,7 +102,7 @@ fn test_builtin_alias_trunk_override_alias() {
let (test_env, workspace_root) = set_up("override-trunk");
test_env.add_config(
r#"revset-aliases.'trunk()' = 'latest(remote_branches(exact:"override-trunk", exact:"origin"))'"#,
r#"revset-aliases.'trunk()' = 'latest(remote_bookmarks(exact:"override-trunk", exact:"origin"))'"#,
);
let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "trunk()"]);

View file

@ -184,12 +184,12 @@ fn test_log_default() {
std::fs::write(repo_path.join("file1"), "foo\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "add a file"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "description 1"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "my-branch"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "my-bookmark"]);
// Test default log output format
let stdout = test_env.jj_cmd_success(&repo_path, &["log"]);
insta::assert_snapshot!(stdout, @r###"
@ kkmpptxz test.user@example.com 2001-02-03 08:05:09 my-branch bac9ff9e
@ kkmpptxz test.user@example.com 2001-02-03 08:05:09 my-bookmark bac9ff9e
(empty) description 1
qpvuntsm test.user@example.com 2001-02-03 08:05:08 aa2015d7
add a file
@ -199,7 +199,7 @@ fn test_log_default() {
// Color
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--color=always"]);
insta::assert_snapshot!(stdout, @r###"
@ kkmpptxz test.user@example.com 2001-02-03 08:05:09 my-branch bac9ff9e
@ kkmpptxz test.user@example.com 2001-02-03 08:05:09 my-bookmark bac9ff9e
(empty) description 1
qpvuntsm test.user@example.com 2001-02-03 08:05:08 aa2015d7
add a file
@ -209,7 +209,7 @@ fn test_log_default() {
// Color without graph
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--color=always", "--no-graph"]);
insta::assert_snapshot!(stdout, @r###"
kkmpptxz test.user@example.com 2001-02-03 08:05:09 my-branch bac9ff9e
kkmpptxz test.user@example.com 2001-02-03 08:05:09 my-bookmark bac9ff9e
(empty) description 1
qpvuntsm test.user@example.com 2001-02-03 08:05:08 aa2015d7
add a file
@ -235,17 +235,17 @@ fn test_log_builtin_templates() {
"new",
],
);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "my-branch"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "my-bookmark"]);
insta::assert_snapshot!(render(r#"builtin_log_oneline"#), @r###"
rlvkpnrz (no email set) 2001-02-03 08:05:08 my-branch dc315397 (empty) (no description set)
rlvkpnrz (no email set) 2001-02-03 08:05:08 my-bookmark dc315397 (empty) (no description set)
qpvuntsm test.user 2001-02-03 08:05:07 230dd059 (empty) (no description set)
zzzzzzzz root() 00000000
[EOF]
"###);
insta::assert_snapshot!(render(r#"builtin_log_compact"#), @r###"
rlvkpnrz (no email set) 2001-02-03 08:05:08 my-branch dc315397
rlvkpnrz (no email set) 2001-02-03 08:05:08 my-bookmark dc315397
(empty) (no description set)
qpvuntsm test.user@example.com 2001-02-03 08:05:07 230dd059
(empty) (no description set)
@ -254,7 +254,7 @@ fn test_log_builtin_templates() {
"###);
insta::assert_snapshot!(render(r#"builtin_log_comfortable"#), @r###"
rlvkpnrz (no email set) 2001-02-03 08:05:08 my-branch dc315397
rlvkpnrz (no email set) 2001-02-03 08:05:08 my-bookmark dc315397
(empty) (no description set)
qpvuntsm test.user@example.com 2001-02-03 08:05:07 230dd059
@ -268,7 +268,7 @@ fn test_log_builtin_templates() {
insta::assert_snapshot!(render(r#"builtin_log_detailed"#), @r###"
Commit ID: dc31539712c7294d1d712cec63cef4504b94ca74
Change ID: rlvkpnrzqnoowoytxnquwvuryrwnrmlp
Branches: my-branch
Bookmarks: my-bookmark
Author: (no name set) <(no email set)> (2001-02-03 08:05:08)
Committer: (no name set) <(no email set)> (2001-02-03 08:05:08)
@ -308,16 +308,16 @@ fn test_log_builtin_templates_colored() {
"new",
],
);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "my-branch"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "my-bookmark"]);
insta::assert_snapshot!(render(r#"builtin_log_oneline"#), @r###"
@ rlvkpnrz (no email set) 2001-02-03 08:05:08 my-branch dc315397 (empty) (no description set)
@ rlvkpnrz (no email set) 2001-02-03 08:05:08 my-bookmark dc315397 (empty) (no description set)
qpvuntsm test.user 2001-02-03 08:05:07 230dd059 (empty) (no description set)
 zzzzzzzz root() 00000000
"###);
insta::assert_snapshot!(render(r#"builtin_log_compact"#), @r###"
@ rlvkpnrz (no email set) 2001-02-03 08:05:08 my-branch dc315397
@ rlvkpnrz (no email set) 2001-02-03 08:05:08 my-bookmark dc315397
(empty) (no description set)
qpvuntsm test.user@example.com 2001-02-03 08:05:07 230dd059
(empty) (no description set)
@ -325,7 +325,7 @@ fn test_log_builtin_templates_colored() {
"###);
insta::assert_snapshot!(render(r#"builtin_log_comfortable"#), @r###"
@ rlvkpnrz (no email set) 2001-02-03 08:05:08 my-branch dc315397
@ rlvkpnrz (no email set) 2001-02-03 08:05:08 my-bookmark dc315397
(empty) (no description set)
qpvuntsm test.user@example.com 2001-02-03 08:05:07 230dd059
@ -338,7 +338,7 @@ fn test_log_builtin_templates_colored() {
insta::assert_snapshot!(render(r#"builtin_log_detailed"#), @r###"
@ Commit ID: dc31539712c7294d1d712cec63cef4504b94ca74
Change ID: rlvkpnrzqnoowoytxnquwvuryrwnrmlp
Branches: my-branch
Bookmarks: my-bookmark
Author: (no name set) <(no email set)> (2001-02-03 08:05:08)
Committer: (no name set) <(no email set)> (2001-02-03 08:05:08)
@ -377,16 +377,16 @@ fn test_log_builtin_templates_colored_debug() {
"new",
],
);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "my-branch"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "my-bookmark"]);
insta::assert_snapshot!(render(r#"builtin_log_oneline"#), @r###"
<<node working_copy::@>> <<log working_copy change_id shortest prefix::r>><<log working_copy change_id shortest rest::lvkpnrz>><<log working_copy:: >><<log working_copy email placeholder::(no email set)>><<log working_copy:: >><<log working_copy committer timestamp local format::2001-02-03 08:05:08>><<log working_copy:: >><<log working_copy branches name::my-branch>><<log working_copy:: >><<log working_copy commit_id shortest prefix::d>><<log working_copy commit_id shortest rest::c315397>><<log working_copy:: >><<log working_copy empty::(empty)>><<log working_copy:: >><<log working_copy empty description placeholder::(no description set)>><<log working_copy::>>
<<node working_copy::@>> <<log working_copy change_id shortest prefix::r>><<log working_copy change_id shortest rest::lvkpnrz>><<log working_copy:: >><<log working_copy email placeholder::(no email set)>><<log working_copy:: >><<log working_copy committer timestamp local format::2001-02-03 08:05:08>><<log working_copy:: >><<log working_copy bookmarks name::my-bookmark>><<log working_copy:: >><<log working_copy commit_id shortest prefix::d>><<log working_copy commit_id shortest rest::c315397>><<log working_copy:: >><<log working_copy empty::(empty)>><<log working_copy:: >><<log working_copy empty description placeholder::(no description set)>><<log working_copy::>>
<<node::>> <<log change_id shortest prefix::q>><<log change_id shortest rest::pvuntsm>><<log:: >><<log author username::test.user>><<log:: >><<log committer timestamp local format::2001-02-03 08:05:07>><<log:: >><<log commit_id shortest prefix::2>><<log commit_id shortest rest::30dd059>><<log:: >><<log empty::(empty)>><<log:: >><<log empty description placeholder::(no description set)>><<log::>>
<<node immutable::>> <<log change_id shortest prefix::z>><<log change_id shortest rest::zzzzzzz>><<log:: >><<log root::root()>><<log:: >><<log commit_id shortest prefix::0>><<log commit_id shortest rest::0000000>><<log::>>
"###);
insta::assert_snapshot!(render(r#"builtin_log_compact"#), @r###"
<<node working_copy::@>> <<log working_copy change_id shortest prefix::r>><<log working_copy change_id shortest rest::lvkpnrz>><<log working_copy:: >><<log working_copy email placeholder::(no email set)>><<log working_copy:: >><<log working_copy committer timestamp local format::2001-02-03 08:05:08>><<log working_copy:: >><<log working_copy branches name::my-branch>><<log working_copy:: >><<log working_copy commit_id shortest prefix::d>><<log working_copy commit_id shortest rest::c315397>><<log working_copy::>>
<<node working_copy::@>> <<log working_copy change_id shortest prefix::r>><<log working_copy change_id shortest rest::lvkpnrz>><<log working_copy:: >><<log working_copy email placeholder::(no email set)>><<log working_copy:: >><<log working_copy committer timestamp local format::2001-02-03 08:05:08>><<log working_copy:: >><<log working_copy bookmarks name::my-bookmark>><<log working_copy:: >><<log working_copy commit_id shortest prefix::d>><<log working_copy commit_id shortest rest::c315397>><<log working_copy::>>
<<log working_copy empty::(empty)>><<log working_copy:: >><<log working_copy empty description placeholder::(no description set)>><<log working_copy::>>
<<node::>> <<log change_id shortest prefix::q>><<log change_id shortest rest::pvuntsm>><<log:: >><<log author email::test.user@example.com>><<log:: >><<log committer timestamp local format::2001-02-03 08:05:07>><<log:: >><<log commit_id shortest prefix::2>><<log commit_id shortest rest::30dd059>><<log::>>
<<log empty::(empty)>><<log:: >><<log empty description placeholder::(no description set)>><<log::>>
@ -394,7 +394,7 @@ fn test_log_builtin_templates_colored_debug() {
"###);
insta::assert_snapshot!(render(r#"builtin_log_comfortable"#), @r###"
<<node working_copy::@>> <<log working_copy change_id shortest prefix::r>><<log working_copy change_id shortest rest::lvkpnrz>><<log working_copy:: >><<log working_copy email placeholder::(no email set)>><<log working_copy:: >><<log working_copy committer timestamp local format::2001-02-03 08:05:08>><<log working_copy:: >><<log working_copy branches name::my-branch>><<log working_copy:: >><<log working_copy commit_id shortest prefix::d>><<log working_copy commit_id shortest rest::c315397>><<log working_copy::>>
<<node working_copy::@>> <<log working_copy change_id shortest prefix::r>><<log working_copy change_id shortest rest::lvkpnrz>><<log working_copy:: >><<log working_copy email placeholder::(no email set)>><<log working_copy:: >><<log working_copy committer timestamp local format::2001-02-03 08:05:08>><<log working_copy:: >><<log working_copy bookmarks name::my-bookmark>><<log working_copy:: >><<log working_copy commit_id shortest prefix::d>><<log working_copy commit_id shortest rest::c315397>><<log working_copy::>>
<<log working_copy empty::(empty)>><<log working_copy:: >><<log working_copy empty description placeholder::(no description set)>><<log working_copy::>>
<<log::>>
<<node::>> <<log change_id shortest prefix::q>><<log change_id shortest rest::pvuntsm>><<log:: >><<log author email::test.user@example.com>><<log:: >><<log committer timestamp local format::2001-02-03 08:05:07>><<log:: >><<log commit_id shortest prefix::2>><<log commit_id shortest rest::30dd059>><<log::>>
@ -407,7 +407,7 @@ fn test_log_builtin_templates_colored_debug() {
insta::assert_snapshot!(render(r#"builtin_log_detailed"#), @r###"
<<node working_copy::@>> <<log::Commit ID: >><<log commit_id::dc31539712c7294d1d712cec63cef4504b94ca74>><<log::>>
<<log::Change ID: >><<log change_id::rlvkpnrzqnoowoytxnquwvuryrwnrmlp>><<log::>>
<<log::Branches: >><<log local_branches name::my-branch>><<log::>>
<<log::Bookmarks: >><<log local_bookmarks name::my-bookmark>><<log::>>
<<log::Author: >><<log name placeholder::(no name set)>><<log:: <>><<log email placeholder::(no email set)>><<log::> (>><<log author timestamp local format::2001-02-03 08:05:08>><<log::)>>
<<log::Committer: >><<log name placeholder::(no name set)>><<log:: <>><<log email placeholder::(no email set)>><<log::> (>><<log committer timestamp local format::2001-02-03 08:05:08>><<log::)>>
<<log::>>
@ -497,7 +497,7 @@ fn test_log_evolog_divergence() {
}
#[test]
fn test_log_branches() {
fn test_log_bookmarks() {
let test_env = TestEnvironment::default();
test_env.add_config("git.auto-local-branch = true");
test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#);
@ -510,13 +510,16 @@ fn test_log_branches() {
.join("store")
.join("git");
// Created some branches on the remote
// Created some bookmarks on the remote
test_env.jj_cmd_ok(&origin_path, &["describe", "-m=description 1"]);
test_env.jj_cmd_ok(&origin_path, &["branch", "create", "branch1"]);
test_env.jj_cmd_ok(&origin_path, &["bookmark", "create", "bookmark1"]);
test_env.jj_cmd_ok(&origin_path, &["new", "root()", "-m=description 2"]);
test_env.jj_cmd_ok(&origin_path, &["branch", "create", "branch2", "unchanged"]);
test_env.jj_cmd_ok(
&origin_path,
&["bookmark", "create", "bookmark2", "unchanged"],
);
test_env.jj_cmd_ok(&origin_path, &["new", "root()", "-m=description 3"]);
test_env.jj_cmd_ok(&origin_path, &["branch", "create", "branch3"]);
test_env.jj_cmd_ok(&origin_path, &["bookmark", "create", "bookmark3"]);
test_env.jj_cmd_ok(&origin_path, &["git", "export"]);
test_env.jj_cmd_ok(
test_env.env_root(),
@ -529,64 +532,64 @@ fn test_log_branches() {
);
let workspace_root = test_env.env_root().join("local");
// Rewrite branch1, move branch2 forward, create conflict in branch3, add
// new-branch
// Rewrite bookmark1, move bookmark2 forward, create conflict in bookmark3, add
// new-bookmark
test_env.jj_cmd_ok(
&workspace_root,
&["describe", "branch1", "-m", "modified branch1 commit"],
&["describe", "bookmark1", "-m", "modified bookmark1 commit"],
);
test_env.jj_cmd_ok(&workspace_root, &["new", "branch2"]);
test_env.jj_cmd_ok(&workspace_root, &["branch", "set", "branch2"]);
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "new-branch"]);
test_env.jj_cmd_ok(&workspace_root, &["describe", "branch3", "-m=local"]);
test_env.jj_cmd_ok(&origin_path, &["describe", "branch3", "-m=origin"]);
test_env.jj_cmd_ok(&workspace_root, &["new", "bookmark2"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "set", "bookmark2"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "new-bookmark"]);
test_env.jj_cmd_ok(&workspace_root, &["describe", "bookmark3", "-m=local"]);
test_env.jj_cmd_ok(&origin_path, &["describe", "bookmark3", "-m=origin"]);
test_env.jj_cmd_ok(&origin_path, &["git", "export"]);
test_env.jj_cmd_ok(&workspace_root, &["git", "fetch"]);
let template = r#"commit_id.short() ++ " " ++ if(branches, branches, "(no branches)")"#;
let template = r#"commit_id.short() ++ " " ++ if(bookmarks, bookmarks, "(no bookmarks)")"#;
let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
insta::assert_snapshot!(output, @r###"
fed794e2ba44 branch3?? branch3@origin
b1bb3766d584 branch3??
fed794e2ba44 bookmark3?? bookmark3@origin
b1bb3766d584 bookmark3??
28ff13ce7195 branch1*
4a7e4246fc4d bookmark1*
@ a5b4d15489cc branch2* new-branch
8476341eb395 branch2@origin unchanged
@ a5b4d15489cc bookmark2* new-bookmark
8476341eb395 bookmark2@origin unchanged
000000000000 (no branches)
000000000000 (no bookmarks)
"###);
let template = r#"branches.map(|b| separate("/", b.remote(), b.name())).join(", ")"#;
let template = r#"bookmarks.map(|b| separate("/", b.remote(), b.name())).join(", ")"#;
let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
insta::assert_snapshot!(output, @r###"
branch3, origin/branch3
branch3
bookmark3, origin/bookmark3
bookmark3
branch1
bookmark1
@ branch2, new-branch
origin/branch2, unchanged
@ bookmark2, new-bookmark
origin/bookmark2, unchanged
"###);
let template = r#"separate(" ", "L:", local_branches, "R:", remote_branches)"#;
let template = r#"separate(" ", "L:", local_bookmarks, "R:", remote_bookmarks)"#;
let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
insta::assert_snapshot!(output, @r###"
L: branch3?? R: branch3@origin
L: branch3?? R:
L: bookmark3?? R: bookmark3@origin
L: bookmark3?? R:
L: branch1* R:
L: bookmark1* R:
@ L: branch2* new-branch R:
L: unchanged R: branch2@origin unchanged@origin
@ L: bookmark2* new-bookmark R:
L: unchanged R: bookmark2@origin unchanged@origin
L: R:
"###);
let template = r#"
remote_branches.map(|ref| concat(
remote_bookmarks.map(|ref| concat(
ref,
if(ref.tracked(),
"(+" ++ ref.tracking_ahead_count().lower()
@ -595,13 +598,13 @@ fn test_log_branches() {
"#;
let output = test_env.jj_cmd_success(
&workspace_root,
&["log", "-r::remote_branches()", "-T", template],
&["log", "-r::remote_bookmarks()", "-T", template],
);
insta::assert_snapshot!(output, @r###"
branch3@origin(+0/-1)
branch2@origin(+0/-1) unchanged@origin(+0/-0)
bookmark3@origin(+0/-1)
bookmark2@origin(+0/-1) unchanged@origin(+0/-0)
branch1@origin(+1/-1)
bookmark1@origin(+1/-1)
"###);
@ -690,14 +693,14 @@ fn test_log_immutable() {
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["new", "-mA", "root()"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-mB"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-mC"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-mD", "root()"]);
let template = r#"
separate(" ",
description.first_line(),
branches,
bookmarks,
if(immutable, "[immutable]"),
) ++ "\n"
"#;
@ -757,7 +760,7 @@ fn test_log_contained_in() {
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["new", "-mA", "root()"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-mB"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-mC"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-mD", "root()"]);
@ -766,7 +769,7 @@ fn test_log_contained_in() {
r#"
separate(" ",
description.first_line(),
branches,
bookmarks,
if(self.contained_in("{revset}"), "[contained_in]"),
) ++ "\n"
"#

View file

@ -365,7 +365,7 @@ fn test_diffedit_merge() {
std::fs::write(repo_path.join("file1"), "a\n").unwrap();
std::fs::write(repo_path.join("file2"), "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(repo_path.join("file1"), "b\n").unwrap();
std::fs::write(repo_path.join("file2"), "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "@-"]);

View file

@ -25,7 +25,7 @@ fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, paren
test_env.jj_cmd_ok(repo_path, &args);
}
std::fs::write(repo_path.join(name), format!("{name}\n")).unwrap();
test_env.jj_cmd_ok(repo_path, &["branch", "create", name]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", name]);
}
#[test]

View file

@ -33,11 +33,11 @@ fn create_commit(
for (name, content) in files {
std::fs::write(repo_path.join(name), content).unwrap();
}
test_env.jj_cmd_ok(repo_path, &["branch", "create", name]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", name]);
}
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["log", "-T", "branches"])
test_env.jj_cmd_success(repo_path, &["log", "-T", "bookmarks"])
}
#[test]

View file

@ -468,13 +468,13 @@ fn test_fix_parent_commit() {
let (test_env, repo_path, redact) = init_with_fake_formatter(&["--uppercase"]);
// Using one file name for all commits adds coverage of some possible bugs.
std::fs::write(repo_path.join("file"), "parent").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "parent"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "parent"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "child1").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "child1"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "child1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-r", "parent"]);
std::fs::write(repo_path.join("file"), "child2").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "child2"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "child2"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["fix", "-s", "parent"]);
insta::assert_snapshot!(stdout, @"");
@ -502,13 +502,13 @@ fn test_fix_parent_commit() {
fn test_fix_sibling_commit() {
let (test_env, repo_path, redact) = init_with_fake_formatter(&["--uppercase"]);
std::fs::write(repo_path.join("file"), "parent").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "parent"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "parent"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "child1").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "child1"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "child1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-r", "parent"]);
std::fs::write(repo_path.join("file"), "child2").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "child2"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "child2"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["fix", "-s", "child1"]);
insta::assert_snapshot!(stdout, @"");
@ -533,22 +533,22 @@ fn test_fix_sibling_commit() {
fn test_default_revset() {
let (test_env, repo_path, redact) = init_with_fake_formatter(&["--uppercase"]);
std::fs::write(repo_path.join("file"), "trunk1").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "trunk1"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "trunk1"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "trunk2").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "trunk2"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "trunk2"]);
test_env.jj_cmd_ok(&repo_path, &["new", "trunk1"]);
std::fs::write(repo_path.join("file"), "foo").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "foo"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "foo"]);
test_env.jj_cmd_ok(&repo_path, &["new", "trunk1"]);
std::fs::write(repo_path.join("file"), "bar1").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "bar1"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "bar1"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "bar2").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "bar2"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "bar2"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "bar3").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "bar3"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "bar3"]);
test_env.jj_cmd_ok(&repo_path, &["edit", "bar2"]);
// With no args and no revset configuration, we fix `reachable(@, mutable())`,
@ -588,10 +588,10 @@ fn test_custom_default_revset() {
let (test_env, repo_path, redact) = init_with_fake_formatter(&["--uppercase"]);
std::fs::write(repo_path.join("file"), "foo").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "foo"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "foo"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "bar").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "bar"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "bar"]);
// Check out a different commit so that the schema default `reachable(@,
// mutable())` would behave differently from our customized default.
@ -619,10 +619,10 @@ fn test_custom_default_revset() {
fn test_fix_immutable_commit() {
let (test_env, repo_path, redact) = init_with_fake_formatter(&["--uppercase"]);
std::fs::write(repo_path.join("file"), "immutable").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "immutable"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "immutable"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "mutable").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "mutable"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "mutable"]);
test_env.add_config(r#"revset-aliases."immutable_heads()" = "immutable""#);
let stderr = test_env.jj_cmd_failure(&repo_path, &["fix", "-s", "immutable"]);
@ -742,16 +742,16 @@ fn test_deduplication() {
// There are at least two interesting cases: the content is repeated immediately
// in the child commit, or later in another descendant.
std::fs::write(repo_path.join("file"), "foo\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "bar\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "bar\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "foo\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "d"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "d"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["fix", "-s", "a"]);
insta::assert_snapshot!(stdout, @"");
@ -971,11 +971,11 @@ fn test_fix_trivial_merge_commit() {
let (test_env, repo_path, redact) = init_with_fake_formatter(&["--uppercase"]);
std::fs::write(repo_path.join("file_a"), "content a").unwrap();
std::fs::write(repo_path.join("file_c"), "content c").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["new", "@-"]);
std::fs::write(repo_path.join("file_b"), "content b").unwrap();
std::fs::write(repo_path.join("file_c"), "content c").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["new", "a", "b"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["fix", "-s", "@"]);
@ -1005,11 +1005,11 @@ fn test_fix_adding_merge_commit() {
let (test_env, repo_path, redact) = init_with_fake_formatter(&["--uppercase"]);
std::fs::write(repo_path.join("file_a"), "content a").unwrap();
std::fs::write(repo_path.join("file_c"), "content c").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["new", "@-"]);
std::fs::write(repo_path.join("file_b"), "content b").unwrap();
std::fs::write(repo_path.join("file_c"), "content c").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["new", "a", "b"]);
std::fs::write(repo_path.join("file_a"), "change a").unwrap();
std::fs::write(repo_path.join("file_b"), "change b").unwrap();
@ -1045,10 +1045,10 @@ fn test_fix_adding_merge_commit() {
fn test_fix_both_sides_of_conflict() {
let (test_env, repo_path, redact) = init_with_fake_formatter(&["--uppercase"]);
std::fs::write(repo_path.join("file"), "content a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["new", "@-"]);
std::fs::write(repo_path.join("file"), "content b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["new", "a", "b"]);
// The conflicts are not different from the merged parent, so they would not be
@ -1095,10 +1095,10 @@ fn test_fix_resolve_conflict() {
// will be resolved.
let (test_env, repo_path, redact) = init_with_fake_formatter(&["--uppercase"]);
std::fs::write(repo_path.join("file"), "Content\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["new", "@-"]);
std::fs::write(repo_path.join("file"), "cOnTeNt\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["new", "a", "b"]);
// The conflicts are not different from the merged parent, so they would not be

View file

@ -234,9 +234,9 @@ fn test_git_clone_colocate() {
Status(IGNORED) .jj/working_copy/
"###);
// The old default branch "master" shouldn't exist.
// The old default bookmark "master" shouldn't exist.
insta::assert_snapshot!(
get_branch_output(&test_env, &test_env.env_root().join("clone")), @r###"
get_bookmark_output(&test_env, &test_env.env_root().join("clone")), @r###"
main: mzyxwzks 9f01a0e0 message
@git: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message
@ -351,12 +351,12 @@ fn test_git_clone_colocate() {
}
#[test]
fn test_git_clone_remote_default_branch() {
fn test_git_clone_remote_default_bookmark() {
let test_env = TestEnvironment::default();
let git_repo_path = test_env.env_root().join("source");
let git_repo = git2::Repository::init(git_repo_path).unwrap();
set_up_non_empty_git_repo(&git_repo);
// Create non-default branch in remote
// Create non-default bookmark in remote
let oid = git_repo
.find_reference("refs/heads/main")
.unwrap()
@ -366,7 +366,7 @@ fn test_git_clone_remote_default_branch() {
.reference("refs/heads/feature1", oid, false, "")
.unwrap();
// All fetched branches will be imported if auto-local-branch is on
// All fetched bookmarks will be imported if auto-local-branch is on
test_env.add_config("git.auto-local-branch = true");
let (_stdout, stderr) =
test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "clone1"]);
@ -380,14 +380,14 @@ fn test_git_clone_remote_default_branch() {
Added 1 files, modified 0 files, removed 0 files
"###);
insta::assert_snapshot!(
get_branch_output(&test_env, &test_env.env_root().join("clone1")), @r###"
get_bookmark_output(&test_env, &test_env.env_root().join("clone1")), @r###"
feature1: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message
main: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message
"###);
// "trunk()" alias should be set to default branch "main"
// "trunk()" alias should be set to default bookmark "main"
let stdout = test_env.jj_cmd_success(
&test_env.env_root().join("clone1"),
&["config", "list", "--repo", "revset-aliases.'trunk()'"],
@ -396,7 +396,7 @@ fn test_git_clone_remote_default_branch() {
revset-aliases.'trunk()' = "main@origin"
"###);
// Only the default branch will be imported if auto-local-branch is off
// Only the default bookmark will be imported if auto-local-branch is off
test_env.add_config("git.auto-local-branch = false");
let (_stdout, stderr) =
test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "clone2"]);
@ -410,13 +410,13 @@ fn test_git_clone_remote_default_branch() {
Added 1 files, modified 0 files, removed 0 files
"###);
insta::assert_snapshot!(
get_branch_output(&test_env, &test_env.env_root().join("clone2")), @r###"
get_bookmark_output(&test_env, &test_env.env_root().join("clone2")), @r###"
feature1@origin: mzyxwzks 9f01a0e0 message
main: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message
"###);
// Change the default branch in remote
// Change the default bookmark in remote
git_repo.set_head("refs/heads/feature1").unwrap();
let (_stdout, stderr) =
test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "clone3"]);
@ -430,13 +430,13 @@ fn test_git_clone_remote_default_branch() {
Added 1 files, modified 0 files, removed 0 files
"###);
insta::assert_snapshot!(
get_branch_output(&test_env, &test_env.env_root().join("clone2")), @r###"
get_bookmark_output(&test_env, &test_env.env_root().join("clone2")), @r###"
feature1@origin: mzyxwzks 9f01a0e0 message
main: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message
"###);
// "trunk()" alias should be set to new default branch "feature1"
// "trunk()" alias should be set to new default bookmark "feature1"
let stdout = test_env.jj_cmd_success(
&test_env.env_root().join("clone3"),
&["config", "list", "--repo", "revset-aliases.'trunk()'"],
@ -498,6 +498,6 @@ fn test_git_clone_at_operation() {
"###);
}
fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["branch", "list", "--all-remotes"])
fn get_bookmark_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["bookmark", "list", "--all-remotes"])
}

View file

@ -94,7 +94,7 @@ fn test_git_colocated() {
}
#[test]
fn test_git_colocated_unborn_branch() {
fn test_git_colocated_unborn_bookmark() {
let test_env = TestEnvironment::default();
let workspace_root = test_env.env_root().join("repo");
let git_repo = git2::Repository::init(&workspace_root).unwrap();
@ -152,7 +152,7 @@ fn test_git_colocated_unborn_branch() {
"###);
// Stage some change, and create new HEAD. This shouldn't move the default
// branch.
// bookmark.
add_file_to_index("file1", "");
let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["new"]);
insta::assert_snapshot!(stdout, @"");
@ -180,8 +180,8 @@ fn test_git_colocated_unborn_branch() {
Parent commit: kkmpptxz e3e01407 (no description set)
"###);
// Assign the default branch. The branch is no longer "unborn".
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "-r@-", "master"]);
// Assign the default bookmark. The bookmark is no longer "unborn".
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "-r@-", "master"]);
// Stage some change, and check out root again. This should unset the HEAD.
// https://github.com/martinvonz/jj/issues/1495
@ -232,8 +232,8 @@ fn test_git_colocated_unborn_branch() {
}
#[test]
fn test_git_colocated_export_branches_on_snapshot() {
// Checks that we export branches that were changed only because the working
fn test_git_colocated_export_bookmarks_on_snapshot() {
// Checks that we export bookmarks that were changed only because the working
// copy was snapshotted
let test_env = TestEnvironment::default();
@ -241,15 +241,15 @@ fn test_git_colocated_export_branches_on_snapshot() {
let git_repo = git2::Repository::init(&workspace_root).unwrap();
test_env.jj_cmd_ok(&workspace_root, &["git", "init", "--git-repo", "."]);
// Create branch pointing to the initial commit
// Create bookmark pointing to the initial commit
std::fs::write(workspace_root.join("file"), "initial").unwrap();
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "foo"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "foo"]);
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ b15ef4cdd277d2c63cce6d67c1916f53a36141f7 foo
0000000000000000000000000000000000000000
"###);
// The branch gets updated when we modify the working copy, and it should get
// The bookmark gets updated when we modify the working copy, and it should get
// exported to Git without requiring any other changes
std::fs::write(workspace_root.join("file"), "modified").unwrap();
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ -275,7 +275,7 @@ fn test_git_colocated_rebase_on_import() {
std::fs::write(workspace_root.join("file"), "contents").unwrap();
test_env.jj_cmd_ok(&workspace_root, &["commit", "-m", "add a file"]);
std::fs::write(workspace_root.join("file"), "modified").unwrap();
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "master"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "master"]);
test_env.jj_cmd_ok(&workspace_root, &["commit", "-m", "modify a file"]);
// TODO: We shouldn't need this command here to trigger an import of the
// refs/heads/master we just exported
@ -309,7 +309,7 @@ fn test_git_colocated_rebase_on_import() {
}
#[test]
fn test_git_colocated_branches() {
fn test_git_colocated_bookmarks() {
let test_env = TestEnvironment::default();
let workspace_root = test_env.env_root().join("repo");
let git_repo = git2::Repository::init(&workspace_root).unwrap();
@ -324,9 +324,9 @@ fn test_git_colocated_branches() {
0000000000000000000000000000000000000000
"###);
// Create a branch in jj. It should be exported to Git even though it points to
// the working- copy commit.
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "master"]);
// Create a bookmark in jj. It should be exported to Git even though it points
// to the working- copy commit.
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "master"]);
insta::assert_snapshot!(
git_repo.find_reference("refs/heads/master").unwrap().target().unwrap().to_string(),
@"3560559274ab431feea00b7b7e0b9250ecce951f"
@ -336,7 +336,7 @@ fn test_git_colocated_branches() {
@"230dd059e1b059aefc0da06a2e5a7dbf22362f22"
);
// Update the branch in Git
// Update the bookmark in Git
let target_id = test_env.jj_cmd_success(
&workspace_root,
&["log", "--no-graph", "-T=commit_id", "-r=description(foo)"],
@ -366,58 +366,65 @@ fn test_git_colocated_branches() {
}
#[test]
fn test_git_colocated_branch_forget() {
fn test_git_colocated_bookmark_forget() {
let test_env = TestEnvironment::default();
let workspace_root = test_env.env_root().join("repo");
let _git_repo = git2::Repository::init(&workspace_root).unwrap();
test_env.jj_cmd_ok(&workspace_root, &["git", "init", "--git-repo", "."]);
test_env.jj_cmd_ok(&workspace_root, &["new"]);
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "foo"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "foo"]);
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ 65b6b74e08973b88d38404430f119c8c79465250 foo
230dd059e1b059aefc0da06a2e5a7dbf22362f22 HEAD@git
0000000000000000000000000000000000000000
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &workspace_root), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &workspace_root), @r###"
foo: rlvkpnrz 65b6b74e (empty) (no description set)
@git: rlvkpnrz 65b6b74e (empty) (no description set)
"###);
let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["branch", "forget", "foo"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["bookmark", "forget", "foo"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Forgot 1 branches.
Forgot 1 bookmarks.
"###);
// A forgotten branch is deleted in the git repo. For a detailed demo explaining
// this, see `test_branch_forget_export` in `test_branch_command.rs`.
insta::assert_snapshot!(get_branch_output(&test_env, &workspace_root), @"");
// A forgotten bookmark is deleted in the git repo. For a detailed demo
// explaining this, see `test_bookmark_forget_export` in
// `test_bookmark_command.rs`.
insta::assert_snapshot!(get_bookmark_output(&test_env, &workspace_root), @"");
}
#[test]
fn test_git_colocated_branch_at_root() {
fn test_git_colocated_bookmark_at_root() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "--colocate", "repo"]);
let repo_path = test_env.env_root().join("repo");
let (_stdout, stderr) =
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "foo", "-r=root()"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "foo", "-r=root()"]);
insta::assert_snapshot!(stderr, @r###"
Created 1 branches pointing to zzzzzzzz 00000000 foo | (empty) (no description set)
Created 1 bookmarks pointing to zzzzzzzz 00000000 foo | (empty) (no description set)
Warning: Failed to export some branches:
foo: Ref cannot point to the root commit in Git
"###);
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "move", "foo"]);
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["bookmark", "move", "foo"]);
insta::assert_snapshot!(stderr, @r###"
Moved 1 branches to qpvuntsm 230dd059 foo | (empty) (no description set)
Moved 1 bookmarks to qpvuntsm 230dd059 foo | (empty) (no description set)
"###);
let (_stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
&["branch", "move", "foo", "--allow-backwards", "--to=root()"],
&[
"bookmark",
"move",
"foo",
"--allow-backwards",
"--to=root()",
],
);
insta::assert_snapshot!(stderr, @r###"
Moved 1 branches to zzzzzzzz 00000000 foo* | (empty) (no description set)
Moved 1 bookmarks to zzzzzzzz 00000000 foo* | (empty) (no description set)
Warning: Failed to export some branches:
foo: Ref cannot point to the root commit in Git
"###);
@ -429,12 +436,12 @@ fn test_git_colocated_conflicting_git_refs() {
let workspace_root = test_env.env_root().join("repo");
git2::Repository::init(&workspace_root).unwrap();
test_env.jj_cmd_ok(&workspace_root, &["git", "init", "--git-repo", "."]);
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "main"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "main/sub"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "main"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "main/sub"]);
insta::assert_snapshot!(stdout, @"");
insta::with_settings!({filters => vec![("Failed to set: .*", "Failed to set: ...")]}, {
insta::assert_snapshot!(stderr, @r###"
Created 1 branches pointing to qpvuntsm 230dd059 main main/sub | (empty) (no description set)
Created 1 bookmarks pointing to qpvuntsm 230dd059 main main/sub | (empty) (no description set)
Warning: Failed to export some branches:
main/sub: Failed to set: ...
Hint: Git doesn't allow a branch name that looks like a parent directory of
@ -507,18 +514,18 @@ fn test_git_colocated_checkout_non_empty_working_copy() {
}
#[test]
fn test_git_colocated_fetch_deleted_or_moved_branch() {
fn test_git_colocated_fetch_deleted_or_moved_bookmark() {
let test_env = TestEnvironment::default();
test_env.add_config("git.auto-local-branch = true");
let origin_path = test_env.env_root().join("origin");
git2::Repository::init(&origin_path).unwrap();
test_env.jj_cmd_ok(&origin_path, &["git", "init", "--git-repo=."]);
test_env.jj_cmd_ok(&origin_path, &["describe", "-m=A"]);
test_env.jj_cmd_ok(&origin_path, &["branch", "create", "A"]);
test_env.jj_cmd_ok(&origin_path, &["bookmark", "create", "A"]);
test_env.jj_cmd_ok(&origin_path, &["new", "-m=B_to_delete"]);
test_env.jj_cmd_ok(&origin_path, &["branch", "create", "B_to_delete"]);
test_env.jj_cmd_ok(&origin_path, &["bookmark", "create", "B_to_delete"]);
test_env.jj_cmd_ok(&origin_path, &["new", "-m=original C", "@-"]);
test_env.jj_cmd_ok(&origin_path, &["branch", "create", "C_to_move"]);
test_env.jj_cmd_ok(&origin_path, &["bookmark", "create", "C_to_move"]);
let clone_path = test_env.env_root().join("clone");
git2::Repository::clone(origin_path.to_str().unwrap(), &clone_path).unwrap();
@ -534,8 +541,8 @@ fn test_git_colocated_fetch_deleted_or_moved_branch() {
0000000000000000000000000000000000000000
"###);
test_env.jj_cmd_ok(&origin_path, &["branch", "delete", "B_to_delete"]);
// Move branch C sideways
test_env.jj_cmd_ok(&origin_path, &["bookmark", "delete", "B_to_delete"]);
// Move bookmark C sideways
test_env.jj_cmd_ok(&origin_path, &["describe", "C_to_move", "-m", "moved C"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&clone_path, &["git", "fetch"]);
insta::assert_snapshot!(stdout, @"");
@ -544,7 +551,7 @@ fn test_git_colocated_fetch_deleted_or_moved_branch() {
branch: C_to_move@origin [updated] tracked
Abandoned 2 commits that are no longer reachable.
"###);
// "original C" and "B_to_delete" are abandoned, as the corresponding branches
// "original C" and "B_to_delete" are abandoned, as the corresponding bookmarks
// were deleted or moved on the remote (#864)
insta::assert_snapshot!(get_log_output(&test_env, &clone_path), @r###"
4f3d13296f978cbc351c46a43b4619c91b888475 C_to_move moved C
@ -565,9 +572,9 @@ fn test_git_colocated_rebase_dirty_working_copy() {
std::fs::write(repo_path.join("file"), "base").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "old").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "feature"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "feature"]);
// Make the working-copy dirty, delete the checked out branch.
// Make the working-copy dirty, delete the checked out bookmark.
std::fs::write(repo_path.join("file"), "new").unwrap();
git_repo
.find_reference("refs/heads/feature")
@ -576,16 +583,16 @@ fn test_git_colocated_rebase_dirty_working_copy() {
.unwrap();
// Because the working copy is dirty, the new working-copy commit will be
// diverged. Therefore, the feature branch has change-delete conflict.
// diverged. Therefore, the feature bookmark has change-delete conflict.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["status"]);
insta::assert_snapshot!(stdout, @r###"
Working copy changes:
M file
Working copy : rlvkpnrz 6bad94b1 feature?? | (no description set)
Parent commit: qpvuntsm 3230d522 (no description set)
These branches have conflicts:
These bookmarks have conflicts:
feature
Use `jj branch list` to see details. Use `jj branch set <name> -r <rev>` to resolve.
Use `jj bookmark list` to see details. Use `jj bookmark set <name> -r <rev>` to resolve.
"###);
insta::assert_snapshot!(stderr, @r###"
Warning: Failed to export some branches:
@ -616,11 +623,11 @@ fn test_git_colocated_external_checkout() {
test_env.jj_cmd_ok(&repo_path, &["git", "init", "--git-repo=."]);
test_env.jj_cmd_ok(&repo_path, &["ci", "-m=A"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "-r@-", "master"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "-r@-", "master"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m=B", "root()"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
// Checked out anonymous branch
// Checked out anonymous bookmark
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ f8a23336e41840ed1757ef323402a770427dc89a
eccedddfa5152d99fc8ddd1081b375387a8a382a HEAD@git B
@ -629,10 +636,10 @@ fn test_git_colocated_external_checkout() {
0000000000000000000000000000000000000000
"###);
// Check out another branch by external command
// Check out another bookmark by external command
git_check_out_ref("refs/heads/master");
// The old working-copy commit gets abandoned, but the whole branch should not
// The old working-copy commit gets abandoned, but the whole bookmark should not
// be abandoned. (#1042)
let (stdout, stderr) = get_log_output_with_stderr(&test_env, &repo_path);
insta::assert_snapshot!(stdout, @r###"
@ -658,7 +665,7 @@ fn test_git_colocated_external_checkout() {
0000000000000000000000000000000000000000
"###);
// Check out another branch by external command
// Check out another bookmark by external command
git_check_out_ref("refs/heads/master");
// The old working-copy commit shouldn't be abandoned. (#3747)
@ -769,7 +776,7 @@ fn get_log_output_divergence(test_env: &TestEnvironment, repo_path: &Path) -> St
change_id.short(),
commit_id.short(),
description.first_line(),
branches,
bookmarks,
git_head,
if(divergent, "!divergence!"),
)
@ -778,7 +785,7 @@ fn get_log_output_divergence(test_env: &TestEnvironment, repo_path: &Path) -> St
}
fn get_log_output(test_env: &TestEnvironment, workspace_root: &Path) -> String {
let template = r#"separate(" ", commit_id, branches, git_head, description)"#;
let template = r#"separate(" ", commit_id, bookmarks, git_head, description)"#;
test_env.jj_cmd_success(workspace_root, &["log", "-T", template, "-r=all()"])
}
@ -786,7 +793,7 @@ fn get_log_output_with_stderr(
test_env: &TestEnvironment,
workspace_root: &Path,
) -> (String, String) {
let template = r#"separate(" ", commit_id, branches, git_head, description)"#;
let template = r#"separate(" ", commit_id, bookmarks, git_head, description)"#;
test_env.jj_cmd_ok(workspace_root, &["log", "-T", template, "-r=all()"])
}
@ -862,7 +869,7 @@ fn test_git_colocated_unreachable_commits() {
"###);
}
fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
// --quiet to suppress deleted branches hint
test_env.jj_cmd_success(repo_path, &["branch", "list", "--all-remotes", "--quiet"])
fn get_bookmark_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
// --quiet to suppress deleted bookmarks hint
test_env.jj_cmd_success(repo_path, &["bookmark", "list", "--all-remotes", "--quiet"])
}

View file

@ -15,7 +15,7 @@ use std::path::Path;
use crate::common::TestEnvironment;
/// Creates a remote Git repo containing a branch with the same name
/// Creates a remote Git repo containing a bookmark with the same name
fn init_git_remote(test_env: &TestEnvironment, remote: &str) {
let git_repo_path = test_env.env_root().join(remote);
let git_repo = git2::Repository::init(git_repo_path).unwrap();
@ -40,7 +40,7 @@ fn init_git_remote(test_env: &TestEnvironment, remote: &str) {
.unwrap();
}
/// Add a remote containing a branch with the same name
/// Add a remote containing a bookmark with the same name
fn add_git_remote(test_env: &TestEnvironment, repo_path: &Path, remote: &str) {
init_git_remote(test_env, remote);
test_env.jj_cmd_ok(
@ -49,9 +49,9 @@ fn add_git_remote(test_env: &TestEnvironment, repo_path: &Path, remote: &str) {
);
}
fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
// --quiet to suppress deleted branches hint
test_env.jj_cmd_success(repo_path, &["branch", "list", "--all-remotes", "--quiet"])
fn get_bookmark_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
// --quiet to suppress deleted bookmarks hint
test_env.jj_cmd_success(repo_path, &["bookmark", "list", "--all-remotes", "--quiet"])
}
fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, parents: &[&str]) {
@ -64,11 +64,11 @@ fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, paren
test_env.jj_cmd_ok(repo_path, &args);
}
std::fs::write(repo_path.join(name), format!("{name}\n")).unwrap();
test_env.jj_cmd_ok(repo_path, &["branch", "create", name]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", name]);
}
fn get_log_output(test_env: &TestEnvironment, workspace_root: &Path) -> String {
let template = r#"commit_id.short() ++ " " ++ description.first_line() ++ " " ++ branches"#;
let template = r#"commit_id.short() ++ " " ++ description.first_line() ++ " " ++ bookmarks"#;
test_env.jj_cmd_success(workspace_root, &["log", "-T", template, "-r", "all()"])
}
@ -80,7 +80,7 @@ fn test_git_fetch_with_default_config() {
add_git_remote(&test_env, &repo_path, "origin");
test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
origin@origin: oputwtnw ffecd2d6 message
"###);
}
@ -94,7 +94,7 @@ fn test_git_fetch_default_remote() {
add_git_remote(&test_env, &repo_path, "origin");
test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
origin: oputwtnw ffecd2d6 message
@origin: oputwtnw ffecd2d6 message
"###);
@ -113,7 +113,7 @@ fn test_git_fetch_single_remote() {
Hint: Fetching from the only existing remote: rem1
branch: rem1@rem1 [new] tracked
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
rem1: qxosxrvv 6a211027 message
@rem1: qxosxrvv 6a211027 message
"###);
@ -131,7 +131,7 @@ fn test_git_fetch_single_remote_all_remotes_flag() {
.jj_cmd(&repo_path, &["git", "fetch", "--all-remotes"])
.assert()
.success();
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
rem1: qxosxrvv 6a211027 message
@rem1: qxosxrvv 6a211027 message
"###);
@ -146,7 +146,7 @@ fn test_git_fetch_single_remote_from_arg() {
add_git_remote(&test_env, &repo_path, "rem1");
test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote", "rem1"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
rem1: qxosxrvv 6a211027 message
@rem1: qxosxrvv 6a211027 message
"###);
@ -162,7 +162,7 @@ fn test_git_fetch_single_remote_from_config() {
test_env.add_config(r#"git.fetch = "rem1""#);
test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
rem1: qxosxrvv 6a211027 message
@rem1: qxosxrvv 6a211027 message
"###);
@ -181,7 +181,7 @@ fn test_git_fetch_multiple_remotes() {
&repo_path,
&["git", "fetch", "--remote", "rem1", "--remote", "rem2"],
);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
rem1: qxosxrvv 6a211027 message
@rem1: qxosxrvv 6a211027 message
rem2: yszkquru 2497a8a0 message
@ -199,7 +199,7 @@ fn test_git_fetch_all_remotes() {
add_git_remote(&test_env, &repo_path, "rem2");
test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--all-remotes"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
rem1: qxosxrvv 6a211027 message
@rem1: qxosxrvv 6a211027 message
rem2: yszkquru 2497a8a0 message
@ -218,7 +218,7 @@ fn test_git_fetch_multiple_remotes_from_config() {
test_env.add_config(r#"git.fetch = ["rem1", "rem2"]"#);
test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
rem1: qxosxrvv 6a211027 message
@rem1: qxosxrvv 6a211027 message
rem2: yszkquru 2497a8a0 message
@ -242,7 +242,7 @@ fn test_git_fetch_nonexistent_remote() {
Error: No git remote named 'rem2'
"###);
// No remote should have been fetched as part of the failing transaction
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
}
#[test]
@ -259,7 +259,7 @@ fn test_git_fetch_nonexistent_remote_from_config() {
Error: No git remote named 'rem2'
"###);
// No remote should have been fetched as part of the failing transaction
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
}
#[test]
@ -283,7 +283,7 @@ fn test_git_fetch_from_remote_named_git() {
"###);
// Implicit import shouldn't fail because of the remote ref.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "list", "--all-remotes"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["bookmark", "list", "--all-remotes"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @"");
@ -297,7 +297,7 @@ fn test_git_fetch_from_remote_named_git() {
// The remote can be renamed, and the ref can be imported.
test_env.jj_cmd_ok(&repo_path, &["git", "remote", "rename", "git", "bar"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "list", "--all-remotes"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["bookmark", "list", "--all-remotes"]);
insta::assert_snapshot!(stdout, @r###"
git: mrylzrtu 76fc7466 message
@bar: mrylzrtu 76fc7466 message
@ -316,12 +316,12 @@ fn test_git_fetch_prune_before_updating_tips() {
let repo_path = test_env.env_root().join("repo");
add_git_remote(&test_env, &repo_path, "origin");
test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
origin: oputwtnw ffecd2d6 message
@origin: oputwtnw ffecd2d6 message
"###);
// Remove origin branch in git repo and create origin/subname
// Remove origin bookmark in git repo and create origin/subname
let git_repo = git2::Repository::open(test_env.env_root().join("origin")).unwrap();
git_repo
.find_branch("origin", git2::BranchType::Local)
@ -330,24 +330,24 @@ fn test_git_fetch_prune_before_updating_tips() {
.unwrap();
test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
origin/subname: oputwtnw ffecd2d6 message
@origin: oputwtnw ffecd2d6 message
"###);
}
#[test]
fn test_git_fetch_conflicting_branches() {
fn test_git_fetch_conflicting_bookmarks() {
let test_env = TestEnvironment::default();
test_env.add_config("git.auto-local-branch = true");
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
add_git_remote(&test_env, &repo_path, "rem1");
// Create a rem1 branch locally
// Create a rem1 bookmark locally
test_env.jj_cmd_ok(&repo_path, &["new", "root()"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "rem1"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "rem1"]);
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
rem1: kkmpptxz fcdbbd73 (empty) (no description set)
"###);
@ -355,8 +355,8 @@ fn test_git_fetch_conflicting_branches() {
&repo_path,
&["git", "fetch", "--remote", "rem1", "--branch", "glob:*"],
);
// This should result in a CONFLICTED branch
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
// This should result in a CONFLICTED bookmark
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
rem1 (conflicted):
+ kkmpptxz fcdbbd73 (empty) (no description set)
+ qxosxrvv 6a211027 message
@ -365,20 +365,20 @@ fn test_git_fetch_conflicting_branches() {
}
#[test]
fn test_git_fetch_conflicting_branches_colocated() {
fn test_git_fetch_conflicting_bookmarks_colocated() {
let test_env = TestEnvironment::default();
test_env.add_config("git.auto-local-branch = true");
let repo_path = test_env.env_root().join("repo");
let _git_repo = git2::Repository::init(&repo_path).unwrap();
// create_colocated_repo_and_branches_from_trunk1(&test_env, &repo_path);
// create_colocated_repo_and_bookmarks_from_trunk1(&test_env, &repo_path);
test_env.jj_cmd_ok(&repo_path, &["git", "init", "--git-repo", "."]);
add_git_remote(&test_env, &repo_path, "rem1");
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
// Create a rem1 branch locally
// Create a rem1 bookmark locally
test_env.jj_cmd_ok(&repo_path, &["new", "root()"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "rem1"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "rem1"]);
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
rem1: zsuskuln f652c321 (empty) (no description set)
@git: zsuskuln f652c321 (empty) (no description set)
"###);
@ -387,9 +387,9 @@ fn test_git_fetch_conflicting_branches_colocated() {
&repo_path,
&["git", "fetch", "--remote", "rem1", "--branch", "rem1"],
);
// This should result in a CONFLICTED branch
// This should result in a CONFLICTED bookmark
// See https://github.com/martinvonz/jj/pull/1146#discussion_r1112372340 for the bug this tests for.
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
rem1 (conflicted):
+ zsuskuln f652c321 (empty) (no description set)
+ qxosxrvv 6a211027 message
@ -398,9 +398,9 @@ fn test_git_fetch_conflicting_branches_colocated() {
"###);
}
// Helper functions to test obtaining multiple branches at once and changed
// branches
fn create_colocated_repo_and_branches_from_trunk1(
// Helper functions to test obtaining multiple bookmarks at once and changed
// bookmarks
fn create_colocated_repo_and_bookmarks_from_trunk1(
test_env: &TestEnvironment,
repo_path: &Path,
) -> String {
@ -416,7 +416,7 @@ fn create_colocated_repo_and_branches_from_trunk1(
)
}
fn create_trunk2_and_rebase_branches(test_env: &TestEnvironment, repo_path: &Path) -> String {
fn create_trunk2_and_rebase_bookmarks(test_env: &TestEnvironment, repo_path: &Path) -> String {
create_commit(test_env, repo_path, "trunk2", &["trunk1"]);
for br in ["a1", "a2", "b"] {
test_env.jj_cmd_ok(repo_path, &["rebase", "-b", br, "-d", "trunk2"]);
@ -446,7 +446,7 @@ fn test_git_fetch_all() {
let target_jj_repo_path = test_env.env_root().join("target");
let source_log =
create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path);
create_colocated_repo_and_bookmarks_from_trunk1(&test_env, &source_git_repo_path);
insta::assert_snapshot!(source_log, @r###"
===== Source git repo contents =====
@ c7d4bdcbc215 descr_for_b b
@ -463,7 +463,7 @@ fn test_git_fetch_all() {
@ 230dd059e1b0
000000000000
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @"");
insta::assert_snapshot!(get_bookmark_output(&test_env, &target_jj_repo_path), @"");
let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
@ -472,7 +472,7 @@ fn test_git_fetch_all() {
branch: b@origin [new] tracked
branch: trunk1@origin [new] tracked
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &target_jj_repo_path), @r###"
a1: nknoxmzm 359a9a02 descr_for_a1
@origin: nknoxmzm 359a9a02 descr_for_a1
a2: qkvnknrk decaa396 descr_for_a2
@ -496,7 +496,7 @@ fn test_git_fetch_all() {
// ==== Change both repos ====
// First, change the target repo:
let source_log = create_trunk2_and_rebase_branches(&test_env, &source_git_repo_path);
let source_log = create_trunk2_and_rebase_bookmarks(&test_env, &source_git_repo_path);
insta::assert_snapshot!(source_log, @r###"
===== Source git repo contents =====
babc49226c14 descr_for_b b
@ -508,7 +508,7 @@ fn test_git_fetch_all() {
ff36dc55760e descr_for_trunk1 trunk1
000000000000
"###);
// Change a branch in the source repo as well, so that it becomes conflicted.
// Change a bookmark in the source repo as well, so that it becomes conflicted.
test_env.jj_cmd_ok(
&target_jj_repo_path,
&["describe", "b", "-m=new_descr_for_b_to_create_conflict"],
@ -526,7 +526,7 @@ fn test_git_fetch_all() {
000000000000
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &target_jj_repo_path), @r###"
a1: nknoxmzm 359a9a02 descr_for_a1
@origin: nknoxmzm 359a9a02 descr_for_a1
a2: qkvnknrk decaa396 descr_for_a2
@ -545,7 +545,7 @@ fn test_git_fetch_all() {
branch: trunk2@origin [new] tracked
Abandoned 2 commits that are no longer reachable.
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &target_jj_repo_path), @r###"
a1: quxllqov 0424f6df descr_for_a1
@origin: quxllqov 0424f6df descr_for_a1
a2: osusxwst 91e46b4b descr_for_a2
@ -577,7 +577,7 @@ fn test_git_fetch_all() {
}
#[test]
fn test_git_fetch_some_of_many_branches() {
fn test_git_fetch_some_of_many_bookmarks() {
let test_env = TestEnvironment::default();
test_env.add_config("git.auto-local-branch = true");
test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#);
@ -595,7 +595,7 @@ fn test_git_fetch_some_of_many_branches() {
let target_jj_repo_path = test_env.env_root().join("target");
let source_log =
create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path);
create_colocated_repo_and_bookmarks_from_trunk1(&test_env, &source_git_repo_path);
insta::assert_snapshot!(source_log, @r###"
===== Source git repo contents =====
@ c7d4bdcbc215 descr_for_b b
@ -626,7 +626,7 @@ fn test_git_fetch_some_of_many_branches() {
@ 230dd059e1b0
000000000000
"###);
// Fetch one branch...
// Fetch one bookmark...
let (stdout, stderr) =
test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "b"]);
insta::assert_snapshot!(stdout, @"");
@ -641,7 +641,7 @@ fn test_git_fetch_some_of_many_branches() {
000000000000
"###);
// ...check what the intermediate state looks like...
insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &target_jj_repo_path), @r###"
b: vpupmnsl c7d4bdcb descr_for_b
@origin: vpupmnsl c7d4bdcb descr_for_b
"###);
@ -666,7 +666,7 @@ fn test_git_fetch_some_of_many_branches() {
000000000000
"###);
// Fetching the same branch again
// Fetching the same bookmark again
let (stdout, stderr) =
test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "a1"]);
insta::assert_snapshot!(stdout, @"");
@ -687,7 +687,7 @@ fn test_git_fetch_some_of_many_branches() {
// ==== Change both repos ====
// First, change the target repo:
let source_log = create_trunk2_and_rebase_branches(&test_env, &source_git_repo_path);
let source_log = create_trunk2_and_rebase_bookmarks(&test_env, &source_git_repo_path);
insta::assert_snapshot!(source_log, @r###"
===== Source git repo contents =====
01d115196c39 descr_for_b b
@ -699,13 +699,13 @@ fn test_git_fetch_some_of_many_branches() {
ff36dc55760e descr_for_trunk1 trunk1
000000000000
"###);
// Change a branch in the source repo as well, so that it becomes conflicted.
// Change a bookmark in the source repo as well, so that it becomes conflicted.
test_env.jj_cmd_ok(
&target_jj_repo_path,
&["describe", "b", "-m=new_descr_for_b_to_create_conflict"],
);
// Our repo before and after fetch of two branches
// Our repo before and after fetch of two bookmarks
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
6ebd41dc4f13 new_descr_for_b_to_create_conflict b*
decaa3966c83 descr_for_a2 a2
@ -742,8 +742,8 @@ fn test_git_fetch_some_of_many_branches() {
000000000000
"###);
// We left a2 where it was before, let's see how `jj branch list` sees this.
insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###"
// We left a2 where it was before, let's see how `jj bookmark list` sees this.
insta::assert_snapshot!(get_bookmark_output(&test_env, &target_jj_repo_path), @r###"
a1: ypowunwp 6df2d34c descr_for_a1
@origin: ypowunwp 6df2d34c descr_for_a1
a2: qkvnknrk decaa396 descr_for_a2
@ -779,7 +779,7 @@ fn test_git_fetch_some_of_many_branches() {
000000000000
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &target_jj_repo_path), @r###"
a1: ypowunwp 6df2d34c descr_for_a1
@origin: ypowunwp 6df2d34c descr_for_a1
a2: qrmzolkr 31c7d94b descr_for_a2
@ -793,7 +793,7 @@ fn test_git_fetch_some_of_many_branches() {
}
// See `test_undo_restore_commands.rs` for fetch-undo-push and fetch-undo-fetch
// of the same branches for various kinds of undo.
// of the same bookmarks for various kinds of undo.
#[test]
fn test_git_fetch_undo() {
let test_env = TestEnvironment::default();
@ -812,7 +812,7 @@ fn test_git_fetch_undo() {
let target_jj_repo_path = test_env.env_root().join("target");
let source_log =
create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path);
create_colocated_repo_and_bookmarks_from_trunk1(&test_env, &source_git_repo_path);
insta::assert_snapshot!(source_log, @r###"
===== Source git repo contents =====
@ c7d4bdcbc215 descr_for_b b
@ -824,7 +824,7 @@ fn test_git_fetch_undo() {
000000000000
"###);
// Fetch 2 branches
// Fetch 2 bookmarks
let (stdout, stderr) = test_env.jj_cmd_ok(
&target_jj_repo_path,
&["git", "fetch", "--branch", "b", "--branch", "a1"],
@ -851,7 +851,7 @@ fn test_git_fetch_undo() {
@ 230dd059e1b0
000000000000
"###);
// Now try to fetch just one branch
// Now try to fetch just one bookmark
let (stdout, stderr) =
test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "b"]);
insta::assert_snapshot!(stdout, @"");
@ -887,7 +887,7 @@ fn test_fetch_undo_what() {
let repo_path = test_env.env_root().join("target");
let source_log =
create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path);
create_colocated_repo_and_bookmarks_from_trunk1(&test_env, &source_git_repo_path);
insta::assert_snapshot!(source_log, @r###"
===== Source git repo contents =====
@ c7d4bdcbc215 descr_for_b b
@ -900,11 +900,11 @@ fn test_fetch_undo_what() {
"###);
// Initial state we will try to return to after `op restore`. There are no
// branches.
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
// bookmarks.
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
let base_operation_id = test_env.current_operation_id(&repo_path);
// Fetch a branch
// Fetch a bookmark
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--branch", "b"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
@ -917,32 +917,33 @@ fn test_fetch_undo_what() {
000000000000
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
b: vpupmnsl c7d4bdcb descr_for_b
@origin: vpupmnsl c7d4bdcb descr_for_b
"###);
// We can undo the change in the repo without moving the remote-tracking branch
// We can undo the change in the repo without moving the remote-tracking
// bookmark
let (stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
&["op", "restore", "--what", "repo", &base_operation_id],
);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @"");
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
b (deleted)
@origin: vpupmnsl hidden c7d4bdcb descr_for_b
"###);
// Now, let's demo restoring just the remote-tracking branch. First, let's
// Now, let's demo restoring just the remote-tracking bookmark. First, let's
// change our local repo state...
test_env.jj_cmd_ok(&repo_path, &["branch", "c", "newbranch"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
test_env.jj_cmd_ok(&repo_path, &["bookmark", "c", "newbookmark"]);
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
b (deleted)
@origin: vpupmnsl hidden c7d4bdcb descr_for_b
newbranch: qpvuntsm 230dd059 (empty) (no description set)
newbookmark: qpvuntsm 230dd059 (empty) (no description set)
"###);
// Restoring just the remote-tracking state will not affect `newbranch`, but
// Restoring just the remote-tracking state will not affect `newbookmark`, but
// will eliminate `b@origin`.
let (stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
@ -956,8 +957,8 @@ fn test_fetch_undo_what() {
);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @"");
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
newbranch: qpvuntsm 230dd059 (empty) (no description set)
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
newbookmark: qpvuntsm 230dd059 (empty) (no description set)
"###);
}
@ -969,13 +970,13 @@ fn test_git_fetch_remove_fetch() {
let repo_path = test_env.env_root().join("repo");
add_git_remote(&test_env, &repo_path, "origin");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "origin"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "origin"]);
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
origin: qpvuntsm 230dd059 (empty) (no description set)
"###);
test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
origin (conflicted):
+ qpvuntsm 230dd059 (empty) (no description set)
+ oputwtnw ffecd2d6 message
@ -983,7 +984,7 @@ fn test_git_fetch_remove_fetch() {
"###);
test_env.jj_cmd_ok(&repo_path, &["git", "remote", "remove", "origin"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
origin (conflicted):
+ qpvuntsm 230dd059 (empty) (no description set)
+ oputwtnw ffecd2d6 message
@ -997,7 +998,7 @@ fn test_git_fetch_remove_fetch() {
insta::assert_snapshot!(stderr, @r###"
branch: origin@origin [new] tracked
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
origin (conflicted):
+ qpvuntsm 230dd059 (empty) (no description set)
+ oputwtnw ffecd2d6 message
@ -1013,13 +1014,13 @@ fn test_git_fetch_rename_fetch() {
let repo_path = test_env.env_root().join("repo");
add_git_remote(&test_env, &repo_path, "origin");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "origin"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "origin"]);
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
origin: qpvuntsm 230dd059 (empty) (no description set)
"###);
test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
origin (conflicted):
+ qpvuntsm 230dd059 (empty) (no description set)
+ oputwtnw ffecd2d6 message
@ -1030,7 +1031,7 @@ fn test_git_fetch_rename_fetch() {
&repo_path,
&["git", "remote", "rename", "origin", "upstream"],
);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
origin (conflicted):
+ qpvuntsm 230dd059 (empty) (no description set)
+ oputwtnw ffecd2d6 message
@ -1047,7 +1048,7 @@ fn test_git_fetch_rename_fetch() {
}
#[test]
fn test_git_fetch_removed_branch() {
fn test_git_fetch_removed_bookmark() {
let test_env = TestEnvironment::default();
test_env.add_config("git.auto-local-branch = true");
let source_git_repo_path = test_env.env_root().join("source");
@ -1064,7 +1065,7 @@ fn test_git_fetch_removed_branch() {
let target_jj_repo_path = test_env.env_root().join("target");
let source_log =
create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path);
create_colocated_repo_and_bookmarks_from_trunk1(&test_env, &source_git_repo_path);
insta::assert_snapshot!(source_log, @r###"
===== Source git repo contents =====
@ c7d4bdcbc215 descr_for_b b
@ -1076,7 +1077,7 @@ fn test_git_fetch_removed_branch() {
000000000000
"###);
// Fetch all branches
// Fetch all bookmarks
let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
@ -1097,10 +1098,10 @@ fn test_git_fetch_removed_branch() {
000000000000
"###);
// Remove a2 branch in origin
test_env.jj_cmd_ok(&source_git_repo_path, &["branch", "forget", "a2"]);
// Remove a2 bookmark in origin
test_env.jj_cmd_ok(&source_git_repo_path, &["bookmark", "forget", "a2"]);
// Fetch branch a1 from origin and check that a2 is still there
// Fetch bookmark a1 from origin and check that a2 is still there
let (stdout, stderr) =
test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "a1"]);
insta::assert_snapshot!(stdout, @"");
@ -1119,7 +1120,7 @@ fn test_git_fetch_removed_branch() {
000000000000
"###);
// Fetch branches a2 from origin, and check that it has been removed locally
// Fetch bookmarks a2 from origin, and check that it has been removed locally
let (stdout, stderr) =
test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "a2"]);
insta::assert_snapshot!(stdout, @"");
@ -1139,7 +1140,7 @@ fn test_git_fetch_removed_branch() {
}
#[test]
fn test_git_fetch_removed_parent_branch() {
fn test_git_fetch_removed_parent_bookmark() {
let test_env = TestEnvironment::default();
test_env.add_config("git.auto-local-branch = true");
let source_git_repo_path = test_env.env_root().join("source");
@ -1156,7 +1157,7 @@ fn test_git_fetch_removed_parent_branch() {
let target_jj_repo_path = test_env.env_root().join("target");
let source_log =
create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path);
create_colocated_repo_and_bookmarks_from_trunk1(&test_env, &source_git_repo_path);
insta::assert_snapshot!(source_log, @r###"
===== Source git repo contents =====
@ c7d4bdcbc215 descr_for_b b
@ -1168,7 +1169,7 @@ fn test_git_fetch_removed_parent_branch() {
000000000000
"###);
// Fetch all branches
// Fetch all bookmarks
let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
@ -1189,11 +1190,11 @@ fn test_git_fetch_removed_parent_branch() {
000000000000
"###);
// Remove all branches in origin.
test_env.jj_cmd_ok(&source_git_repo_path, &["branch", "forget", "glob:*"]);
// Remove all bookmarks in origin.
test_env.jj_cmd_ok(&source_git_repo_path, &["bookmark", "forget", "glob:*"]);
// Fetch branches master, trunk1 and a1 from origin and check that only those
// branches have been removed and that others were not rebased because of
// Fetch bookmarks master, trunk1 and a1 from origin and check that only those
// bookmarks have been removed and that others were not rebased because of
// abandoned commits.
let (stdout, stderr) = test_env.jj_cmd_ok(
&target_jj_repo_path,
@ -1219,7 +1220,7 @@ fn test_git_fetch_removed_parent_branch() {
}
#[test]
fn test_git_fetch_remote_only_branch() {
fn test_git_fetch_remote_only_bookmark() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
@ -1240,7 +1241,7 @@ fn test_git_fetch_remote_only_branch() {
&repo_path,
&["git", "remote", "add", "origin", "../git-repo"],
);
// Create a commit and a branch in the git repo
// Create a commit and a bookmark in the git repo
git_repo
.commit(
Some("refs/heads/feature1"),
@ -1252,10 +1253,10 @@ fn test_git_fetch_remote_only_branch() {
)
.unwrap();
// Fetch using git.auto_local_branch = true
// Fetch using git.auto_local_bookmark = true
test_env.add_config("git.auto-local-branch = true");
test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote=origin"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
feature1: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message
"###);
@ -1271,7 +1272,7 @@ fn test_git_fetch_remote_only_branch() {
)
.unwrap();
// Fetch using git.auto_local_branch = false
// Fetch using git.auto_local_bookmark = false
test_env.add_config("git.auto-local-branch = false");
test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote=origin"]);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ -1280,7 +1281,7 @@ fn test_git_fetch_remote_only_branch() {
000000000000
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
feature1: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message
feature2@origin: mzyxwzks 9f01a0e0 message

View file

@ -19,20 +19,20 @@ use jj_lib::backend::CommitId;
use crate::common::TestEnvironment;
#[test]
fn test_resolution_of_git_tracking_branches() {
fn test_resolution_of_git_tracking_bookmarks() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-r", "main", "-m", "old_message"]);
// Create local-git tracking branch
// Create local-git tracking bookmark
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "export"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @"");
// Move the local branch somewhere else
// Move the local bookmark somewhere else
test_env.jj_cmd_ok(&repo_path, &["describe", "-r", "main", "-m", "new_message"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm b61d21b6 (empty) new_message
@git (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 03757d22 (empty) old_message
"###);
@ -51,8 +51,8 @@ fn test_resolution_of_git_tracking_branches() {
insta::assert_snapshot!(query("main@git"), @r###"
03757d2212d89990ec158e97795b612a38446652 old_message
"###);
// Can't be selected by remote_branches()
insta::assert_snapshot!(query(r#"remote_branches(exact:"main", exact:"git")"#), @"");
// Can't be selected by remote_bookmarks()
insta::assert_snapshot!(query(r#"remote_bookmarks(exact:"main", exact:"git")"#), @"");
}
#[test]
@ -61,8 +61,8 @@ fn test_git_export_conflicting_git_refs() {
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main/sub"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main/sub"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "export"]);
insta::assert_snapshot!(stdout, @"");
insta::with_settings!({filters => vec![("Failed to set: .*", "Failed to set: ...")]}, {
@ -83,8 +83,8 @@ fn test_git_export_undo() {
let repo_path = test_env.env_root().join("repo");
let git_repo = git2::Repository::open(repo_path.join(".jj/repo/store/git")).unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
a: qpvuntsm 230dd059 (empty) (no description set)
"###);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "export"]);
@ -97,7 +97,7 @@ fn test_git_export_undo() {
"###);
// Exported refs won't be removed by undoing the export, but the git-tracking
// branch is. This is the same as remote-tracking branches.
// bookmark is. This is the same as remote-tracking bookmarks.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "undo"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @"");
@ -116,7 +116,7 @@ fn test_git_export_undo() {
Hint: Did you mean "a"?
"###);
// This would re-export branch "a" and create git-tracking branch.
// This would re-export bookmark "a" and create git-tracking bookmark.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "export"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @"");
@ -134,7 +134,7 @@ fn test_git_import_undo() {
let repo_path = test_env.env_root().join("repo");
let git_repo = git2::Repository::open(repo_path.join(".jj/repo/store/git")).unwrap();
// Create branch "a" in git repo
// Create bookmark "a" in git repo
let commit_id =
test_env.jj_cmd_success(&repo_path, &["log", "-Tcommit_id", "--no-graph", "-r@"]);
let commit = git_repo
@ -142,8 +142,8 @@ fn test_git_import_undo() {
.unwrap();
git_repo.branch("a", &commit, true).unwrap();
// Initial state we will return to after `undo`. There are no branches.
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
// Initial state we will return to after `undo`. There are no bookmarks.
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
let base_operation_id = test_env.current_operation_id(&repo_path);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "import"]);
@ -151,7 +151,7 @@ fn test_git_import_undo() {
insta::assert_snapshot!(stderr, @r###"
branch: a [new] tracked
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
a: qpvuntsm 230dd059 (empty) (no description set)
@git: qpvuntsm 230dd059 (empty) (no description set)
"###);
@ -160,14 +160,14 @@ fn test_git_import_undo() {
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "restore", &base_operation_id]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @"");
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
// Try "git import" again, which should re-import the branch "a".
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
// Try "git import" again, which should re-import the bookmark "a".
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "import"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
branch: a [new] tracked
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
a: qpvuntsm 230dd059 (empty) (no description set)
@git: qpvuntsm 230dd059 (empty) (no description set)
"###);
@ -180,7 +180,7 @@ fn test_git_import_move_export_with_default_undo() {
let repo_path = test_env.env_root().join("repo");
let git_repo = git2::Repository::open(repo_path.join(".jj/repo/store/git")).unwrap();
// Create branch "a" in git repo
// Create bookmark "a" in git repo
let commit_id =
test_env.jj_cmd_success(&repo_path, &["log", "-Tcommit_id", "--no-graph", "-r@"]);
let commit = git_repo
@ -189,8 +189,8 @@ fn test_git_import_move_export_with_default_undo() {
git_repo.branch("a", &commit, true).unwrap();
// Initial state we will try to return to after `op restore`. There are no
// branches.
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
// bookmarks.
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
let base_operation_id = test_env.current_operation_id(&repo_path);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "import"]);
@ -198,28 +198,28 @@ fn test_git_import_move_export_with_default_undo() {
insta::assert_snapshot!(stderr, @r###"
branch: a [new] tracked
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
a: qpvuntsm 230dd059 (empty) (no description set)
@git: qpvuntsm 230dd059 (empty) (no description set)
"###);
// Move branch "a" and export to git repo
// Move bookmark "a" and export to git repo
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "set", "a"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
test_env.jj_cmd_ok(&repo_path, &["bookmark", "set", "a"]);
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
a: yqosqzyt 096dc80d (empty) (no description set)
@git (behind by 1 commits): qpvuntsm 230dd059 (empty) (no description set)
"###);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "export"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @"");
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
a: yqosqzyt 096dc80d (empty) (no description set)
@git: yqosqzyt 096dc80d (empty) (no description set)
"###);
// "git import" can be undone with the default `restore` behavior, as shown in
// the previous test. However, "git export" can't: the branches in the git
// the previous test. However, "git export" can't: the bookmarks in the git
// repo stay where they were.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "restore", &base_operation_id]);
insta::assert_snapshot!(stdout, @"");
@ -227,7 +227,7 @@ fn test_git_import_move_export_with_default_undo() {
Working copy now at: qpvuntsm 230dd059 (empty) (no description set)
Parent commit : zzzzzzzz 00000000 (empty) (no description set)
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @"");
insta::assert_debug_snapshot!(get_git_repo_refs(&git_repo), @r###"
[
(
@ -239,21 +239,21 @@ fn test_git_import_move_export_with_default_undo() {
]
"###);
// The last branch "a" state is imported from git. No idea what's the most
// The last bookmark "a" state is imported from git. No idea what's the most
// intuitive result here.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "import"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
branch: a [new] tracked
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
a: yqosqzyt 096dc80d (empty) (no description set)
@git: yqosqzyt 096dc80d (empty) (no description set)
"###);
}
fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["branch", "list", "--all-remotes"])
fn get_bookmark_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["bookmark", "list", "--all-remotes"])
}
fn get_git_repo_refs(git_repo: &git2::Repository) -> Vec<(String, CommitId)> {

View file

@ -46,7 +46,7 @@ fn init_git_repo_with_opts(
.unwrap();
git_repo
.commit(
Some("refs/heads/my-branch"),
Some("refs/heads/my-bookmark"),
&git_signature,
&git_signature,
"My commit message",
@ -55,16 +55,16 @@ fn init_git_repo_with_opts(
)
.unwrap();
drop(git_tree);
git_repo.set_head("refs/heads/my-branch").unwrap();
git_repo.set_head("refs/heads/my-bookmark").unwrap();
git_repo
}
fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["branch", "list", "--all-remotes"])
fn get_bookmark_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["bookmark", "list", "--all-remotes"])
}
fn get_log_output(test_env: &TestEnvironment, workspace_root: &Path) -> String {
let template = r#"separate(" ", commit_id.short(), branches, git_head, description)"#;
let template = r#"separate(" ", commit_id.short(), bookmarks, git_head, description)"#;
test_env.jj_cmd_success(workspace_root, &["log", "-T", template, "-r=all()"])
}
@ -144,7 +144,7 @@ fn test_git_init_external(bare: bool) {
insta::assert_snapshot!(stderr, @r###"
Done importing changes from the underlying Git repo.
Working copy now at: sqpuoqvx f6950fc1 (empty) (no description set)
Parent commit : mwrttmos 8d698d4a my-branch | My commit message
Parent commit : mwrttmos 8d698d4a my-bookmark | My commit message
Added 1 files, modified 0 files, removed 0 files
Initialized repo in "repo"
"###);
@ -170,7 +170,7 @@ fn test_git_init_external(bare: bool) {
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ f6950fc115ae
8d698d4a8ee1 my-branch HEAD@git My commit message
8d698d4a8ee1 my-bookmark HEAD@git My commit message
000000000000
"###);
}
@ -183,9 +183,9 @@ fn test_git_init_external_import_trunk(bare: bool) {
let git_repo_path = test_env.env_root().join("git-repo");
let git_repo = init_git_repo(&git_repo_path, bare);
// Add remote branch "trunk" for remote "origin", and set it as "origin/HEAD"
// Add remote bookmark "trunk" for remote "origin", and set it as "origin/HEAD"
let oid = git_repo
.find_reference("refs/heads/my-branch")
.find_reference("refs/heads/my-bookmark")
.unwrap()
.target()
.unwrap();
@ -217,13 +217,13 @@ fn test_git_init_external_import_trunk(bare: bool) {
Done importing changes from the underlying Git repo.
Setting the revset alias "trunk()" to "trunk@origin"
Working copy now at: sqpuoqvx f6950fc1 (empty) (no description set)
Parent commit : mwrttmos 8d698d4a my-branch trunk@origin | My commit message
Parent commit : mwrttmos 8d698d4a my-bookmark trunk@origin | My commit message
Added 1 files, modified 0 files, removed 0 files
Initialized repo in "repo"
"###);
}
// "trunk()" alias should be set to remote "origin"'s default branch "trunk"
// "trunk()" alias should be set to remote "origin"'s default bookmark "trunk"
let stdout = test_env.jj_cmd_success(
&test_env.env_root().join("repo"),
&["config", "list", "--repo", "revset-aliases.\"trunk()\""],
@ -344,7 +344,7 @@ fn test_git_init_colocated_via_git_repo_path() {
// Check that the Git repo's HEAD got checked out
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ f61b77cd4bb5
8d698d4a8ee1 my-branch HEAD@git My commit message
8d698d4a8ee1 my-bookmark HEAD@git My commit message
000000000000
"###);
@ -353,7 +353,7 @@ fn test_git_init_colocated_via_git_repo_path() {
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ f1c7aa7c62d8
f61b77cd4bb5 HEAD@git
8d698d4a8ee1 my-branch My commit message
8d698d4a8ee1 my-bookmark My commit message
000000000000
"###);
}
@ -380,7 +380,7 @@ fn test_git_init_colocated_via_git_repo_path_gitlink() {
// Check that the Git repo's HEAD got checked out
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ f61b77cd4bb5
8d698d4a8ee1 my-branch HEAD@git My commit message
8d698d4a8ee1 my-bookmark HEAD@git My commit message
000000000000
"###);
@ -389,7 +389,7 @@ fn test_git_init_colocated_via_git_repo_path_gitlink() {
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ f1c7aa7c62d8
f61b77cd4bb5 HEAD@git
8d698d4a8ee1 my-branch My commit message
8d698d4a8ee1 my-bookmark My commit message
000000000000
"###);
}
@ -415,7 +415,7 @@ fn test_git_init_colocated_via_git_repo_path_symlink_directory() {
// Check that the Git repo's HEAD got checked out
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ f61b77cd4bb5
8d698d4a8ee1 my-branch HEAD@git My commit message
8d698d4a8ee1 my-bookmark HEAD@git My commit message
000000000000
"###);
@ -424,7 +424,7 @@ fn test_git_init_colocated_via_git_repo_path_symlink_directory() {
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ f1c7aa7c62d8
f61b77cd4bb5 HEAD@git
8d698d4a8ee1 my-branch My commit message
8d698d4a8ee1 my-bookmark My commit message
000000000000
"###);
}
@ -453,7 +453,7 @@ fn test_git_init_colocated_via_git_repo_path_symlink_directory_without_bare_conf
// Check that the Git repo's HEAD got checked out
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ f61b77cd4bb5
8d698d4a8ee1 my-branch HEAD@git My commit message
8d698d4a8ee1 my-bookmark HEAD@git My commit message
000000000000
"###);
@ -462,7 +462,7 @@ fn test_git_init_colocated_via_git_repo_path_symlink_directory_without_bare_conf
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ f1c7aa7c62d8
f61b77cd4bb5 HEAD@git
8d698d4a8ee1 my-branch My commit message
8d698d4a8ee1 my-bookmark My commit message
000000000000
"###);
}
@ -493,7 +493,7 @@ fn test_git_init_colocated_via_git_repo_path_symlink_gitlink() {
// Check that the Git repo's HEAD got checked out
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ f61b77cd4bb5
8d698d4a8ee1 my-branch HEAD@git My commit message
8d698d4a8ee1 my-bookmark HEAD@git My commit message
000000000000
"###);
@ -502,7 +502,7 @@ fn test_git_init_colocated_via_git_repo_path_symlink_gitlink() {
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ f1c7aa7c62d8
f61b77cd4bb5 HEAD@git
8d698d4a8ee1 my-branch My commit message
8d698d4a8ee1 my-bookmark My commit message
000000000000
"###);
}
@ -517,7 +517,7 @@ fn test_git_init_colocated_via_git_repo_path_imported_refs() {
let remote_path = test_env.env_root().join("remote");
test_env.jj_cmd_ok(
&remote_path,
&["branch", "create", "local-remote", "remote-only"],
&["bookmark", "create", "local-remote", "remote-only"],
);
test_env.jj_cmd_ok(&remote_path, &["new"]);
test_env.jj_cmd_ok(&remote_path, &["git", "export"]);
@ -547,7 +547,7 @@ fn test_git_init_colocated_via_git_repo_path_imported_refs() {
Done importing changes from the underlying Git repo.
Initialized repo in "."
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &local_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &local_path), @r###"
local-remote: vvkvtnvv 230dd059 (empty) (no description set)
@git: vvkvtnvv 230dd059 (empty) (no description set)
@origin: vvkvtnvv 230dd059 (empty) (no description set)
@ -563,12 +563,12 @@ fn test_git_init_colocated_via_git_repo_path_imported_refs() {
let (_stdout, stderr) = test_env.jj_cmd_ok(&local_path, &["git", "init", "--git-repo=."]);
insta::assert_snapshot!(stderr, @r###"
Done importing changes from the underlying Git repo.
Hint: The following remote branches aren't associated with the existing local branches:
Hint: The following remote bookmarks aren't associated with the existing local bookmarks:
local-remote@origin
Hint: Run `jj branch track local-remote@origin` to keep local branches updated on future pulls.
Hint: Run `jj bookmark track local-remote@origin` to keep local bookmarks updated on future pulls.
Initialized repo in "."
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &local_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &local_path), @r###"
local-remote: vvkvtnvv 230dd059 (empty) (no description set)
@git: vvkvtnvv 230dd059 (empty) (no description set)
local-remote@origin: vvkvtnvv 230dd059 (empty) (no description set)
@ -620,7 +620,7 @@ fn test_git_init_colocated_dirty_working_copy() {
C {some-file => new-staged-file}
M some-file
C {some-file => unstaged-file}
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-bookmark HEAD@git 8d698d4a
My commit message
A some-file
zzzzzzzz root() 00000000
@ -719,7 +719,7 @@ fn test_git_init_colocated_via_flag_git_dir_exists() {
// Check that the Git repo's HEAD got checked out
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ f61b77cd4bb5
8d698d4a8ee1 my-branch HEAD@git My commit message
8d698d4a8ee1 my-bookmark HEAD@git My commit message
000000000000
"###);
@ -728,7 +728,7 @@ fn test_git_init_colocated_via_flag_git_dir_exists() {
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ f1c7aa7c62d8
f61b77cd4bb5 HEAD@git
8d698d4a8ee1 my-branch My commit message
8d698d4a8ee1 my-bookmark My commit message
000000000000
"###);
}
@ -749,10 +749,10 @@ fn test_git_init_colocated_via_flag_git_dir_not_exists() {
000000000000
"###);
// Create the default branch (create both in case we change the default)
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "main", "master"]);
// Create the default bookmark (create both in case we change the default)
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "main", "master"]);
// If .git/HEAD pointed to the default branch, new working-copy commit would
// If .git/HEAD pointed to the default bookmark, new working-copy commit would
// be created on top.
insta::assert_snapshot!(get_log_output(&test_env, &workspace_root), @r###"
@ 230dd059e1b0 main master

View file

@ -29,7 +29,7 @@ fn set_up() -> (TestEnvironment, PathBuf) {
test_env.jj_cmd_ok(&origin_path, &["describe", "-m=public 1"]);
test_env.jj_cmd_ok(&origin_path, &["new", "-m=public 2"]);
test_env.jj_cmd_ok(&origin_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&origin_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&origin_path, &["git", "export"]);
test_env.jj_cmd_ok(
@ -76,7 +76,7 @@ fn test_git_private_commits_block_pushing() {
let (test_env, workspace_root) = set_up();
test_env.jj_cmd_ok(&workspace_root, &["new", "main", "-m=private 1"]);
test_env.jj_cmd_ok(&workspace_root, &["branch", "set", "main"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "set", "main"]);
// Will not push when a pushed commit is contained in git.private-commits
test_env.add_config(r#"git.private-commits = "description(glob:'private*')""#);
@ -90,7 +90,7 @@ fn test_git_private_commits_block_pushing() {
let (_, stderr) = test_env.jj_cmd_ok(&workspace_root, &["git", "push", "--all"]);
insta::assert_snapshot!(stderr, @r###"
Branch changes to push to origin:
Move forward branch main from 7eb97bf230ad to aa3058ff8663
Move forward bookmark main from 7eb97bf230ad to aa3058ff8663
Warning: The working-copy commit in workspace 'default' became immutable, so a new commit has been created on top of it.
Working copy now at: znkkpsqq 2e1adf47 (empty) (no description set)
Parent commit : yqosqzyt aa3058ff main | (empty) private 1
@ -102,7 +102,7 @@ fn test_git_private_commits_can_be_overridden() {
let (test_env, workspace_root) = set_up();
test_env.jj_cmd_ok(&workspace_root, &["new", "main", "-m=private 1"]);
test_env.jj_cmd_ok(&workspace_root, &["branch", "set", "main"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "set", "main"]);
// Will not push when a pushed commit is contained in git.private-commits
test_env.add_config(r#"git.private-commits = "description(glob:'private*')""#);
@ -118,7 +118,7 @@ fn test_git_private_commits_can_be_overridden() {
);
insta::assert_snapshot!(stderr, @r###"
Branch changes to push to origin:
Move forward branch main from 7eb97bf230ad to aa3058ff8663
Move forward bookmark main from 7eb97bf230ad to aa3058ff8663
Warning: The working-copy commit in workspace 'default' became immutable, so a new commit has been created on top of it.
Working copy now at: znkkpsqq 2e1adf47 (empty) (no description set)
Parent commit : yqosqzyt aa3058ff main | (empty) private 1
@ -130,14 +130,14 @@ fn test_git_private_commits_are_not_checked_if_immutable() {
let (test_env, workspace_root) = set_up();
test_env.jj_cmd_ok(&workspace_root, &["new", "main", "-m=private 1"]);
test_env.jj_cmd_ok(&workspace_root, &["branch", "set", "main"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "set", "main"]);
test_env.add_config(r#"git.private-commits = "description(glob:'private*')""#);
test_env.add_config(r#"revset-aliases."immutable_heads()" = "all()""#);
let (_, stderr) = test_env.jj_cmd_ok(&workspace_root, &["git", "push", "--all"]);
insta::assert_snapshot!(stderr, @r###"
Branch changes to push to origin:
Move forward branch main from 7eb97bf230ad to aa3058ff8663
Move forward bookmark main from 7eb97bf230ad to aa3058ff8663
Warning: The working-copy commit in workspace 'default' became immutable, so a new commit has been created on top of it.
Working copy now at: yostqsxw dce4a15c (empty) (no description set)
Parent commit : yqosqzyt aa3058ff main | (empty) private 1
@ -152,10 +152,10 @@ fn test_git_private_commits_not_directly_in_line_block_pushing() {
test_env.jj_cmd_ok(&workspace_root, &["new", "root()", "-m=private 1"]);
test_env.jj_cmd_ok(&workspace_root, &["new", "main", "@", "-m=public 3"]);
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "branch1"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "bookmark1"]);
test_env.add_config(r#"git.private-commits = "description(glob:'private*')""#);
let stderr = test_env.jj_cmd_failure(&workspace_root, &["git", "push", "-b=branch1"]);
let stderr = test_env.jj_cmd_failure(&workspace_root, &["git", "push", "-b=bookmark1"]);
insta::assert_snapshot!(stderr, @r###"
Error: Won't push commit f1253a9b1ea9 since it is private
"###);
@ -166,14 +166,14 @@ fn test_git_private_commits_descending_from_commits_pushed_do_not_block_pushing(
let (test_env, workspace_root) = set_up();
test_env.jj_cmd_ok(&workspace_root, &["new", "main", "-m=public 3"]);
test_env.jj_cmd_ok(&workspace_root, &["branch", "move", "main"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "move", "main"]);
test_env.jj_cmd_ok(&workspace_root, &["new", "-m=private 1"]);
test_env.add_config(r#"git.private-commits = "description(glob:'private*')""#);
let (_, stderr) = test_env.jj_cmd_ok(&workspace_root, &["git", "push", "-b=main"]);
insta::assert_snapshot!(stderr, @r###"
Branch changes to push to origin:
Move forward branch main from 7eb97bf230ad to 05ef53bc99ec
Move forward bookmark main from 7eb97bf230ad to 05ef53bc99ec
"###);
}
@ -181,20 +181,23 @@ fn test_git_private_commits_descending_from_commits_pushed_do_not_block_pushing(
fn test_git_private_commits_already_on_the_remote_do_not_block_push() {
let (test_env, workspace_root) = set_up();
// Start a branch before a "private" commit lands in main
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "branch1", "-r=main"]);
// Start a bookmark before a "private" commit lands in main
test_env.jj_cmd_ok(
&workspace_root,
&["bookmark", "create", "bookmark1", "-r=main"],
);
// Push a commit that would become a private_root if it weren't already on
// the remote
test_env.jj_cmd_ok(&workspace_root, &["new", "main", "-m=private 1"]);
test_env.jj_cmd_ok(&workspace_root, &["new", "-m=public 3"]);
test_env.jj_cmd_ok(&workspace_root, &["branch", "set", "main"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "set", "main"]);
let (_, stderr) =
test_env.jj_cmd_ok(&workspace_root, &["git", "push", "-b=main", "-b=branch1"]);
test_env.jj_cmd_ok(&workspace_root, &["git", "push", "-b=main", "-b=bookmark1"]);
insta::assert_snapshot!(stderr, @r###"
Branch changes to push to origin:
Move forward branch main from 7eb97bf230ad to fbb352762352
Add branch branch1 to 7eb97bf230ad
Move forward bookmark main from 7eb97bf230ad to fbb352762352
Add bookmark bookmark1 to 7eb97bf230ad
Warning: The working-copy commit in workspace 'default' became immutable, so a new commit has been created on top of it.
Working copy now at: kpqxywon a7b08364 (empty) (no description set)
Parent commit : yostqsxw fbb35276 main | (empty) public 3
@ -203,24 +206,27 @@ fn test_git_private_commits_already_on_the_remote_do_not_block_push() {
test_env.add_config(r#"git.private-commits = "description(glob:'private*')""#);
// Since "private 1" is already on the remote, pushing it should be allowed
test_env.jj_cmd_ok(&workspace_root, &["branch", "set", "branch1", "-r=main"]);
test_env.jj_cmd_ok(
&workspace_root,
&["bookmark", "set", "bookmark1", "-r=main"],
);
let (_, stderr) = test_env.jj_cmd_ok(&workspace_root, &["git", "push", "--all"]);
insta::assert_snapshot!(stderr, @r###"
Branch changes to push to origin:
Move forward branch branch1 from 7eb97bf230ad to fbb352762352
Move forward bookmark bookmark1 from 7eb97bf230ad to fbb352762352
"###);
// Ensure that the already-pushed commit doesn't block a new branch from
// Ensure that the already-pushed commit doesn't block a new bookmark from
// being pushed
test_env.jj_cmd_ok(
&workspace_root,
&["new", "description('private 1')", "-m=public 4"],
);
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "branch2"]);
let (_, stderr) = test_env.jj_cmd_ok(&workspace_root, &["git", "push", "-b=branch2"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "bookmark2"]);
let (_, stderr) = test_env.jj_cmd_ok(&workspace_root, &["git", "push", "-b=bookmark2"]);
insta::assert_snapshot!(stderr, @r###"
Branch changes to push to origin:
Add branch branch2 to ee5b808b0b95
Add bookmark bookmark2 to ee5b808b0b95
"###);
}
@ -234,11 +240,11 @@ fn test_git_private_commits_are_evaluated_separately_for_each_remote() {
// the remote
test_env.jj_cmd_ok(&workspace_root, &["new", "main", "-m=private 1"]);
test_env.jj_cmd_ok(&workspace_root, &["new", "-m=public 3"]);
test_env.jj_cmd_ok(&workspace_root, &["branch", "set", "main"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "set", "main"]);
let (_, stderr) = test_env.jj_cmd_ok(&workspace_root, &["git", "push", "-b=main"]);
insta::assert_snapshot!(stderr, @r###"
Branch changes to push to origin:
Move forward branch main from 7eb97bf230ad to d8632ce893ab
Move forward bookmark main from 7eb97bf230ad to d8632ce893ab
"###);
test_env.add_config(r#"git.private-commits = "description(glob:'private*')""#);

File diff suppressed because it is too large Load diff

View file

@ -192,7 +192,7 @@ fn test_git_remote_named_git() {
.remote("git", "http://example.com/repo/repo")
.unwrap();
test_env.jj_cmd_ok(&repo_path, &["git", "init", "--git-repo=."]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
// The remote can be renamed.
let (stdout, stderr) =
@ -203,8 +203,8 @@ fn test_git_remote_named_git() {
insta::assert_snapshot!(stdout, @r###"
bar http://example.com/repo/repo
"###);
// @git branch shouldn't be renamed.
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-rmain@git", "-Tbranches"]);
// @git bookmark shouldn't be renamed.
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-rmain@git", "-Tbookmarks"]);
insta::assert_snapshot!(stdout, @r###"
@ main
@ -229,8 +229,8 @@ fn test_git_remote_named_git() {
let stdout = test_env.jj_cmd_success(&repo_path, &["git", "remote", "list"]);
insta::assert_snapshot!(stdout, @r###"
"###);
// @git branch shouldn't be removed.
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-rmain@git", "-Tbranches"]);
// @git bookmark shouldn't be removed.
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-rmain@git", "-Tbookmarks"]);
insta::assert_snapshot!(stdout, @r###"
main

View file

@ -104,7 +104,7 @@ fn test_gitignores_ignored_file_in_target_commit() {
// Create a commit with file "ignored" in it
std::fs::write(workspace_root.join("ignored"), "committed contents\n").unwrap();
test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "with-file"]);
test_env.jj_cmd_ok(&workspace_root, &["bookmark", "create", "with-file"]);
let target_commit_id = test_env.jj_cmd_success(
&workspace_root,
&["log", "--no-graph", "-T=commit_id", "-r=@"],

View file

@ -89,9 +89,9 @@ fn test_no_subcommand() {
assert_eq!(stdout, test_env.jj_cmd_success(&repo_path, &["log"]));
// Command argument that looks like a command name.
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "help"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "log"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "show"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "help"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "log"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "show"]);
// TODO: test_env.jj_cmd_ok(&repo_path, &["-r", "help"])
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["-r", "log"]), @r###"
@ qpvuntsm test.user@example.com 2001-02-03 08:05:07 help log show 230dd059

View file

@ -23,7 +23,7 @@ fn test_rewrite_immutable_generic() {
test_env.jj_cmd_ok(&repo_path, &["describe", "-m=a"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m=b"]);
std::fs::write(repo_path.join("file"), "b").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["new", "main-", "-m=c"]);
std::fs::write(repo_path.join("file"), "c").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["log"]);
@ -59,11 +59,11 @@ fn test_rewrite_immutable_generic() {
// Error mutating the repo if immutable_heads() uses a ref that can't be
// resolved
test_env.add_config(r#"revset-aliases."immutable_heads()" = "branch_that_does_not_exist""#);
test_env.add_config(r#"revset-aliases."immutable_heads()" = "bookmark_that_does_not_exist""#);
let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "main"]);
insta::assert_snapshot!(stderr, @r###"
Config error: Invalid `revset-aliases.immutable_heads()`
Caused by: Revision "branch_that_does_not_exist" doesn't exist
Caused by: Revision "bookmark_that_does_not_exist" doesn't exist
For help, see https://martinvonz.github.io/jj/latest/config/.
"###);
@ -85,7 +85,7 @@ fn test_rewrite_immutable_generic() {
// Mutating the repo works if ref is wrapped in present()
test_env.add_config(
r#"revset-aliases."immutable_heads()" = "present(branch_that_does_not_exist)""#,
r#"revset-aliases."immutable_heads()" = "present(bookmark_that_does_not_exist)""#,
);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["new", "main"]);
insta::assert_snapshot!(stdout, @r###"
@ -107,12 +107,12 @@ fn test_rewrite_immutable_generic() {
fn test_new_wc_commit_when_wc_immutable() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init"]);
test_env.jj_cmd_ok(test_env.env_root(), &["branch", "create", "main"]);
test_env.jj_cmd_ok(test_env.env_root(), &["bookmark", "create", "main"]);
test_env.add_config(r#"revset-aliases."immutable_heads()" = "main""#);
test_env.jj_cmd_ok(test_env.env_root(), &["new", "-m=a"]);
let (_, stderr) = test_env.jj_cmd_ok(test_env.env_root(), &["branch", "set", "main"]);
let (_, stderr) = test_env.jj_cmd_ok(test_env.env_root(), &["bookmark", "set", "main"]);
insta::assert_snapshot!(stderr, @r###"
Moved 1 branches to kkmpptxz a164195b main | (empty) a
Moved 1 bookmarks to kkmpptxz a164195b main | (empty) a
Warning: The working-copy commit in workspace 'default' became immutable, so a new commit has been created on top of it.
Working copy now at: zsuskuln ef5fa85b (empty) (no description set)
Parent commit : kkmpptxz a164195b main | (empty) a
@ -123,7 +123,7 @@ fn test_new_wc_commit_when_wc_immutable() {
fn test_immutable_heads_set_to_working_copy() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init"]);
test_env.jj_cmd_ok(test_env.env_root(), &["branch", "create", "main"]);
test_env.jj_cmd_ok(test_env.env_root(), &["bookmark", "create", "main"]);
test_env.add_config(r#"revset-aliases."immutable_heads()" = "@""#);
let (_, stderr) = test_env.jj_cmd_ok(test_env.env_root(), &["new", "-m=a"]);
insta::assert_snapshot!(stderr, @r###"
@ -138,15 +138,15 @@ fn test_new_wc_commit_when_wc_immutable_multi_workspace() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.add_config(r#"revset-aliases."immutable_heads()" = "main""#);
test_env.jj_cmd_ok(&repo_path, &["new", "-m=a"]);
test_env.jj_cmd_ok(&repo_path, &["workspace", "add", "../workspace1"]);
let workspace1_envroot = test_env.env_root().join("workspace1");
test_env.jj_cmd_ok(&workspace1_envroot, &["edit", "default@"]);
let (_, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "set", "main"]);
let (_, stderr) = test_env.jj_cmd_ok(&repo_path, &["bookmark", "set", "main"]);
insta::assert_snapshot!(stderr, @r###"
Moved 1 branches to kkmpptxz 7796c4df main | (empty) a
Moved 1 bookmarks to kkmpptxz 7796c4df main | (empty) a
Warning: The working-copy commit in workspace 'default' became immutable, so a new commit has been created on top of it.
Warning: The working-copy commit in workspace 'workspace1' became immutable, so a new commit has been created on top of it.
Working copy now at: royxmykx 896465c4 (empty) (no description set)
@ -180,7 +180,7 @@ fn test_rewrite_immutable_commands() {
// Create another file to make sure the merge commit isn't empty (to satisfy `jj
// split`) and still has a conflict (to satisfy `jj resolve`).
std::fs::write(repo_path.join("file2"), "merged").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["new", "description(b)"]);
test_env.add_config(r#"revset-aliases."immutable_heads()" = "main""#);
test_env.add_config(r#"revset-aliases."trunk()" = "main""#);

View file

@ -45,7 +45,7 @@ fn init_git_repo_with_opts(
.unwrap();
git_repo
.commit(
Some("refs/heads/my-branch"),
Some("refs/heads/my-bookmark"),
&git_signature,
&git_signature,
"My commit message",
@ -54,12 +54,12 @@ fn init_git_repo_with_opts(
)
.unwrap();
drop(git_tree);
git_repo.set_head("refs/heads/my-branch").unwrap();
git_repo.set_head("refs/heads/my-bookmark").unwrap();
git_repo
}
fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["branch", "list", "--all-remotes"])
fn get_bookmark_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["bookmark", "list", "--all-remotes"])
}
fn read_git_target(workspace_root: &Path) -> String {
@ -113,7 +113,7 @@ fn test_init_git_external(bare: bool) {
insta::assert_snapshot!(stderr, @r###"
Done importing changes from the underlying Git repo.
Working copy now at: sqpuoqvx f6950fc1 (empty) (no description set)
Parent commit : mwrttmos 8d698d4a my-branch | My commit message
Parent commit : mwrttmos 8d698d4a my-bookmark | My commit message
Added 1 files, modified 0 files, removed 0 files
Warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` instead
@ -141,7 +141,7 @@ fn test_init_git_external(bare: bool) {
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-r", "@-"]);
insta::allow_duplicates! {
insta::assert_snapshot!(stdout, @r###"
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-bookmark HEAD@git 8d698d4a
My commit message
~
"###);
@ -209,7 +209,7 @@ fn test_init_git_colocated() {
// Check that the Git repo's HEAD got checked out
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-r", "@-"]);
insta::assert_snapshot!(stdout, @r###"
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-bookmark HEAD@git 8d698d4a
My commit message
~
"###);
@ -248,7 +248,7 @@ fn test_init_git_colocated_gitlink() {
// Check that the Git repo's HEAD got checked out
let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]);
insta::assert_snapshot!(stdout, @r###"
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-bookmark HEAD@git 8d698d4a
My commit message
~
"###);
@ -286,7 +286,7 @@ fn test_init_git_colocated_symlink_directory() {
// Check that the Git repo's HEAD got checked out
let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]);
insta::assert_snapshot!(stdout, @r###"
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-bookmark HEAD@git 8d698d4a
My commit message
~
"###);
@ -327,7 +327,7 @@ fn test_init_git_colocated_symlink_directory_without_bare_config() {
// Check that the Git repo's HEAD got checked out
let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]);
insta::assert_snapshot!(stdout, @r###"
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-bookmark HEAD@git 8d698d4a
My commit message
~
"###);
@ -370,7 +370,7 @@ fn test_init_git_colocated_symlink_gitlink() {
// Check that the Git repo's HEAD got checked out
let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]);
insta::assert_snapshot!(stdout, @r###"
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a
mwrttmos git.user@example.com 1970-01-01 11:02:03 my-bookmark HEAD@git 8d698d4a
My commit message
~
"###);
@ -395,7 +395,7 @@ fn test_init_git_colocated_imported_refs() {
let remote_path = test_env.env_root().join("remote");
test_env.jj_cmd_ok(
&remote_path,
&["branch", "create", "local-remote", "remote-only"],
&["bookmark", "create", "local-remote", "remote-only"],
);
test_env.jj_cmd_ok(&remote_path, &["new"]);
test_env.jj_cmd_ok(&remote_path, &["git", "export"]);
@ -427,7 +427,7 @@ fn test_init_git_colocated_imported_refs() {
Use `jj git init` instead
Initialized repo in "."
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &local_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &local_path), @r###"
local-remote: vvkvtnvv 230dd059 (empty) (no description set)
@git: vvkvtnvv 230dd059 (empty) (no description set)
@origin: vvkvtnvv 230dd059 (empty) (no description set)
@ -443,14 +443,14 @@ fn test_init_git_colocated_imported_refs() {
let (_stdout, stderr) = test_env.jj_cmd_ok(&local_path, &["init", "--git-repo=."]);
insta::assert_snapshot!(stderr, @r###"
Done importing changes from the underlying Git repo.
Hint: The following remote branches aren't associated with the existing local branches:
Hint: The following remote bookmarks aren't associated with the existing local bookmarks:
local-remote@origin
Hint: Run `jj branch track local-remote@origin` to keep local branches updated on future pulls.
Hint: Run `jj bookmark track local-remote@origin` to keep local bookmarks updated on future pulls.
Warning: `--git` and `--git-repo` are deprecated.
Use `jj git init` instead
Initialized repo in "."
"###);
insta::assert_snapshot!(get_branch_output(&test_env, &local_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &local_path), @r###"
local-remote: vvkvtnvv 230dd059 (empty) (no description set)
@git: vvkvtnvv 230dd059 (empty) (no description set)
local-remote@origin: vvkvtnvv 230dd059 (empty) (no description set)

View file

@ -23,13 +23,13 @@ fn test_interdiff_basic() {
std::fs::write(repo_path.join("file1"), "foo\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file2"), "foo\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "left"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "left"]);
test_env.jj_cmd_ok(&repo_path, &["checkout", "root()"]);
std::fs::write(repo_path.join("file3"), "foo\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file2"), "foo\nbar\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "right"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "right"]);
// implicit --to
let stdout = test_env.jj_cmd_success(&repo_path, &["interdiff", "--from", "left"]);
@ -87,7 +87,7 @@ fn test_interdiff_paths() {
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file1"), "bar\n").unwrap();
std::fs::write(repo_path.join("file2"), "bar\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "left"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "left"]);
test_env.jj_cmd_ok(&repo_path, &["checkout", "root()"]);
std::fs::write(repo_path.join("file1"), "foo\n").unwrap();
@ -95,7 +95,7 @@ fn test_interdiff_paths() {
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file1"), "baz\n").unwrap();
std::fs::write(repo_path.join("file2"), "baz\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "right"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "right"]);
let stdout = test_env.jj_cmd_success(
&repo_path,
@ -135,13 +135,13 @@ fn test_interdiff_conflicting() {
std::fs::write(repo_path.join("file"), "foo\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "bar\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "left"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "left"]);
test_env.jj_cmd_ok(&repo_path, &["checkout", "root()"]);
std::fs::write(repo_path.join("file"), "abc\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
std::fs::write(repo_path.join("file"), "def\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "right"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "right"]);
let stdout = test_env.jj_cmd_success(
&repo_path,

View file

@ -351,7 +351,7 @@ fn test_log_shortest_accessors() {
std::fs::write(repo_path.join("file"), "original file\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "initial"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "c", "original"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "c", "original"]);
insta::assert_snapshot!(
render("original", r#"format_id(change_id) ++ " " ++ format_id(commit_id)"#),
@"q[pvuntsmwlqt] e[0e22b9fae75]");
@ -474,7 +474,7 @@ fn test_log_prefix_highlight_styled() {
change_id.shortest({0}),
description.first_line(),
commit_id.shortest({0}),
branches,
bookmarks,
)
"###,
len.map(|l| l.to_string()).unwrap_or_default()
@ -483,7 +483,7 @@ fn test_log_prefix_highlight_styled() {
std::fs::write(repo_path.join("file"), "original file\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "initial"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "c", "original"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "c", "original"]);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["log", "-r", "original", "-T", &prefix_format(Some(12))]),
@r###"
@ -611,13 +611,13 @@ fn test_log_prefix_highlight_counts_hidden_commits() {
format_id(change_id),
description.first_line(),
format_id(commit_id),
branches,
bookmarks,
)
"#;
std::fs::write(repo_path.join("file"), "original file\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "initial"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "c", "original"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "c", "original"]);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["log", "-r", "all()", "-T", prefix_format]),
@r###"
@ -1097,21 +1097,21 @@ fn test_multiple_revsets() {
let repo_path = test_env.env_root().join("repo");
for name in ["foo", "bar", "baz"] {
test_env.jj_cmd_ok(&repo_path, &["new", "-m", name]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", name]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", name]);
}
// Default revset should be overridden if one or more -r options are specified.
test_env.add_config(r#"revsets.log = "root()""#);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["log", "-T", "branches", "-rfoo"]),
test_env.jj_cmd_success(&repo_path, &["log", "-T", "bookmarks", "-rfoo"]),
@r###"
foo
~
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["log", "-T", "branches", "-rfoo", "-rbar", "-rbaz"]),
test_env.jj_cmd_success(&repo_path, &["log", "-T", "bookmarks", "-rfoo", "-rbar", "-rbaz"]),
@r###"
@ baz
bar
@ -1120,7 +1120,7 @@ fn test_multiple_revsets() {
~
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["log", "-T", "branches", "-rfoo", "-rfoo"]),
test_env.jj_cmd_success(&repo_path, &["log", "-T", "bookmarks", "-rfoo", "-rfoo"]),
@r###"
foo
@ -1184,15 +1184,21 @@ fn test_graph_styles() {
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "initial"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "main branch 1"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "main branch 2"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "main bookmark 1"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "main bookmark 2"]);
test_env.jj_cmd_ok(
&repo_path,
&["new", "-m", "side branch\nwith\nlong\ndescription"],
&["new", "-m", "side bookmark\nwith\nlong\ndescription"],
);
test_env.jj_cmd_ok(
&repo_path,
&["new", "-m", "merge", r#"description("main branch 1")"#, "@"],
&[
"new",
"-m",
"merge",
r#"description("main bookmark 1")"#,
"@",
],
);
// Default (curved) style
@ -1200,13 +1206,13 @@ fn test_graph_styles() {
insta::assert_snapshot!(stdout, @r###"
@ merge
side branch
side bookmark
with
long
description
main branch 2
main bookmark 2
main branch 1
main bookmark 1
initial
"###);
@ -1217,13 +1223,13 @@ fn test_graph_styles() {
insta::assert_snapshot!(stdout, @r###"
@ merge
|\
| o side branch
| o side bookmark
| | with
| | long
| | description
| o main branch 2
| o main bookmark 2
|/
o main branch 1
o main bookmark 1
o initial
+
"###);
@ -1235,14 +1241,14 @@ fn test_graph_styles() {
@ merge
|\
| \
| o side branch
| o side bookmark
| | with
| | long
| | description
| o main branch 2
| o main bookmark 2
| /
|/
o main branch 1
o main bookmark 1
o initial
+
"###);
@ -1253,13 +1259,13 @@ fn test_graph_styles() {
insta::assert_snapshot!(stdout, @r###"
@ merge
side branch
side bookmark
with
long
description
main branch 2
main bookmark 2
main branch 1
main bookmark 1
initial
"###);
@ -1270,13 +1276,13 @@ fn test_graph_styles() {
insta::assert_snapshot!(stdout, @r###"
@ merge
side branch
side bookmark
with
long
description
main branch 2
main bookmark 2
main branch 1
main bookmark 1
initial
"###);
@ -1311,36 +1317,36 @@ fn test_log_word_wrap() {
get_stdout_string(&assert)
};
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "main branch 1"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "main branch 2"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "main bookmark 1"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "main bookmark 2"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "side"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "merge", "@--", "@"]);
// ui.log-word-wrap option applies to both graph/no-graph outputs
insta::assert_snapshot!(render(&["log", "-r@"], 40, false), @r###"
@ mzvwutvl test.user@example.com 2001-02-03 08:05:11 044c0400
@ mzvwutvl test.user@example.com 2001-02-03 08:05:11 f3efbd00
(empty) merge
~
"###);
insta::assert_snapshot!(render(&["log", "-r@"], 40, true), @r###"
@ mzvwutvl test.user@example.com
2001-02-03 08:05:11 044c0400
2001-02-03 08:05:11 f3efbd00
~ (empty) merge
"###);
insta::assert_snapshot!(render(&["log", "--no-graph", "-r@"], 40, false), @r###"
mzvwutvl test.user@example.com 2001-02-03 08:05:11 044c0400
mzvwutvl test.user@example.com 2001-02-03 08:05:11 f3efbd00
(empty) merge
"###);
insta::assert_snapshot!(render(&["log", "--no-graph", "-r@"], 40, true), @r###"
mzvwutvl test.user@example.com
2001-02-03 08:05:11 044c0400
2001-02-03 08:05:11 f3efbd00
(empty) merge
"###);
// Color labels should be preserved
insta::assert_snapshot!(render(&["log", "-r@", "--color=always"], 40, true), @r###"
@ mzvwutvl test.user@example.com
2001-02-03 08:05:11 044c0400
2001-02-03 08:05:11 f3efbd00
~ (empty) merge
"###);
@ -1373,7 +1379,7 @@ fn test_log_word_wrap() {
test.user@example.com
~ 2001-02-03
08:05:11
044c0400
f3efbd00
(empty)
merge
"###);
@ -1382,7 +1388,7 @@ fn test_log_word_wrap() {
test.user@example.com
~ 2001-02-03
08:05:11
044c0400
f3efbd00
(empty)
merge
"###);
@ -1443,13 +1449,19 @@ fn test_elided() {
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "initial"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "main branch 1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "main branch 2"]);
test_env.jj_cmd_ok(&repo_path, &["new", "@--", "-m", "side branch 1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "side branch 2"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "main bookmark 1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "main bookmark 2"]);
test_env.jj_cmd_ok(&repo_path, &["new", "@--", "-m", "side bookmark 1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "side bookmark 2"]);
test_env.jj_cmd_ok(
&repo_path,
&["new", "-m", "merge", r#"description("main branch 2")"#, "@"],
&[
"new",
"-m",
"merge",
r#"description("main bookmark 2")"#,
"@",
],
);
let get_log = |revs: &str| -> String {
@ -1463,13 +1475,13 @@ fn test_elided() {
insta::assert_snapshot!(get_log("::"), @r###"
@ merge
side branch 2
side bookmark 2
side branch 1
side bookmark 1
main branch 2
main bookmark 2
main branch 1
main bookmark 1
initial
@ -1482,9 +1494,9 @@ fn test_elided() {
insta::assert_snapshot!(get_log("@ | @- | description(initial)"), @r###"
@ merge
side branch 2
side bookmark 2
main branch 2
main bookmark 2
initial
@ -1494,9 +1506,9 @@ fn test_elided() {
// Elide shared commits. It's unclear that a revision was skipped on the right
// side (#1252).
insta::assert_snapshot!(get_log("@-- | root()"), @r###"
side branch 1
side bookmark 1
main branch 1
main bookmark 1
"###);
@ -1508,10 +1520,10 @@ fn test_elided() {
insta::assert_snapshot!(get_log("@ | @- | description(initial)"), @r###"
@ merge
side branch 2
side bookmark 2
~ (elided revisions)
main branch 2
main bookmark 2
~ (elided revisions)
@ -1523,10 +1535,10 @@ fn test_elided() {
// Elide shared commits. To keep the implementation simple, it still gets
// rendered as two synthetic nodes.
insta::assert_snapshot!(get_log("@-- | root()"), @r###"
side branch 1
side bookmark 1
~ (elided revisions)
main branch 1
main bookmark 1
~ (elided revisions)
@ -1542,13 +1554,19 @@ fn test_log_with_custom_symbols() {
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "initial"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "main branch 1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "main branch 2"]);
test_env.jj_cmd_ok(&repo_path, &["new", "@--", "-m", "side branch 1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "side branch 2"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "main bookmark 1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "main bookmark 2"]);
test_env.jj_cmd_ok(&repo_path, &["new", "@--", "-m", "side bookmark 1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "side bookmark 2"]);
test_env.jj_cmd_ok(
&repo_path,
&["new", "-m", "merge", r#"description("main branch 2")"#, "@"],
&[
"new",
"-m",
"merge",
r#"description("main bookmark 2")"#,
"@",
],
);
let get_log = |revs: &str| -> String {
@ -1568,10 +1586,10 @@ fn test_log_with_custom_symbols() {
insta::assert_snapshot!(get_log("@ | @- | description(initial) | root()"), @r###"
$ merge
side branch 2
side bookmark 2
🮀 (elided revisions)
main branch 2
main bookmark 2
🮀 (elided revisions)
@ -1591,10 +1609,10 @@ fn test_log_with_custom_symbols() {
insta::assert_snapshot!(get_log("@ | @- | description(initial) | root()"), @r###"
$ merge
|\
| * side branch 2
| * side bookmark 2
| |
| : (elided revisions)
* | main branch 2
* | main bookmark 2
| |
: | (elided revisions)
|/

View file

@ -34,25 +34,25 @@ fn test_move() {
//
// When moving changes between e.g. C and F, we should not get unrelated changes
// from B and D.
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
std::fs::write(repo_path.join("file1"), "a\n").unwrap();
std::fs::write(repo_path.join("file2"), "a\n").unwrap();
std::fs::write(repo_path.join("file3"), "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(repo_path.join("file3"), "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
std::fs::write(repo_path.join("file1"), "c\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["edit", "a"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "d"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "d"]);
std::fs::write(repo_path.join("file3"), "d\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "e"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "e"]);
std::fs::write(repo_path.join("file2"), "e\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "f"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "f"]);
std::fs::write(repo_path.join("file2"), "f\n").unwrap();
// Test the setup
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ -183,20 +183,20 @@ fn test_move_partial() {
// D B
// |/
// A
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
std::fs::write(repo_path.join("file1"), "a\n").unwrap();
std::fs::write(repo_path.join("file2"), "a\n").unwrap();
std::fs::write(repo_path.join("file3"), "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(repo_path.join("file3"), "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
std::fs::write(repo_path.join("file1"), "c\n").unwrap();
std::fs::write(repo_path.join("file2"), "c\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["edit", "a"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "d"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "d"]);
std::fs::write(repo_path.join("file3"), "d\n").unwrap();
// Test the setup
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ -358,7 +358,7 @@ fn test_move_partial() {
}
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
let template = r#"commit_id.short() ++ " " ++ branches"#;
let template = r#"commit_id.short() ++ " " ++ bookmarks"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}

View file

@ -69,7 +69,7 @@ fn test_new_merge() {
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "add file1"]);
std::fs::write(repo_path.join("file1"), "a").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "root()", "-m", "add file2"]);
@ -617,19 +617,19 @@ fn test_new_insert_after_before_no_loop() {
}
#[test]
fn test_new_conflicting_branches() {
fn test_new_conflicting_bookmarks() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "one"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "two", "@-"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "foo"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "foo"]);
test_env.jj_cmd_ok(
&repo_path,
&[
"--at-op=@-",
"branch",
"bookmark",
"create",
"foo",
"-r",
@ -689,19 +689,19 @@ fn test_new_error_revision_does_not_exist() {
}
fn setup_before_insertion(test_env: &TestEnvironment, repo_path: &Path) {
test_env.jj_cmd_ok(repo_path, &["branch", "create", "A"]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", "A"]);
test_env.jj_cmd_ok(repo_path, &["commit", "-m", "A"]);
test_env.jj_cmd_ok(repo_path, &["branch", "create", "B"]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", "B"]);
test_env.jj_cmd_ok(repo_path, &["commit", "-m", "B"]);
test_env.jj_cmd_ok(repo_path, &["branch", "create", "C"]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", "C"]);
test_env.jj_cmd_ok(repo_path, &["describe", "-m", "C"]);
test_env.jj_cmd_ok(repo_path, &["new", "-m", "D", "root()"]);
test_env.jj_cmd_ok(repo_path, &["branch", "create", "D"]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", "D"]);
test_env.jj_cmd_ok(repo_path, &["new", "-m", "E", "root()"]);
test_env.jj_cmd_ok(repo_path, &["branch", "create", "E"]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", "E"]);
// Any number of -r's is ignored
test_env.jj_cmd_ok(repo_path, &["new", "-m", "F", "-r", "D", "-r", "E"]);
test_env.jj_cmd_ok(repo_path, &["branch", "create", "F"]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", "F"]);
}
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {

View file

@ -209,7 +209,7 @@ fn test_next_exceeding_history() {
}
// The working copy commit is a child of a "fork" with two children on each
// branch.
// bookmark.
#[test]
fn test_next_parent_has_multiple_descendants() {
let test_env = TestEnvironment::default();
@ -332,7 +332,7 @@ fn test_next_on_merge_commit() {
}
#[test]
fn test_next_fails_on_branching_children_no_stdin() {
fn test_next_fails_on_bookmarking_children_no_stdin() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
@ -361,7 +361,7 @@ fn test_next_fails_on_branching_children_no_stdin() {
}
#[test]
fn test_next_fails_on_branching_children_quit_prompt() {
fn test_next_fails_on_bookmarking_children_quit_prompt() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
@ -401,7 +401,7 @@ fn test_next_fails_on_branching_children_quit_prompt() {
}
#[test]
fn test_next_choose_branching_child() {
fn test_next_choose_bookmarking_child() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
@ -434,9 +434,9 @@ fn test_prev_on_merge_commit() {
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["desc", "-m", "first"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "c", "left"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "c", "left"]);
test_env.jj_cmd_ok(&repo_path, &["new", "root()", "-m", "second"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "c", "right"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "c", "right"]);
test_env.jj_cmd_ok(&repo_path, &["new", "left", "right"]);
// Check that the graph looks the way we expect.
@ -1113,6 +1113,6 @@ fn test_movement_edit_mode_false() {
}
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
let template = r#"separate(" ", change_id.short(), local_branches, if(conflict, "conflict"), description)"#;
let template = r#"separate(" ", change_id.short(), local_bookmarks, if(conflict, "conflict"), description)"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}

View file

@ -702,10 +702,10 @@ fn test_op_diff() {
// Overview of op log.
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]);
insta::assert_snapshot!(&stdout, @r###"
@ 984d5ceb039f test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
@ ea112f6a02be test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
check out git remote's default branch
args: jj git clone git-repo repo
817baaeefcbb test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
cba9d7096849 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
fetch from git remote into empty repo
args: jj git clone git-repo repo
b51416386f26 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
@ -727,8 +727,8 @@ fn test_op_diff() {
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff", "--from", "@", "--to", "@"]);
insta::assert_snapshot!(&stdout, @r###"
From operation 984d5ceb039f: check out git remote's default branch
To operation 984d5ceb039f: check out git remote's default branch
From operation ea112f6a02be: check out git remote's default branch
To operation ea112f6a02be: check out git remote's default branch
"###);
// Diff from parent operation to latest operation.
@ -736,8 +736,8 @@ fn test_op_diff() {
// @- --to @` (if `@` is not a merge commit).
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff", "--from", "@-", "--to", "@"]);
insta::assert_snapshot!(&stdout, @r###"
From operation 817baaeefcbb: fetch from git remote into empty repo
To operation 984d5ceb039f: check out git remote's default branch
From operation cba9d7096849: fetch from git remote into empty repo
To operation ea112f6a02be: check out git remote's default branch
Changed commits:
Change sqpuoqvxutmz
@ -746,14 +746,14 @@ fn test_op_diff() {
- qpvuntsm hidden 230dd059 (empty) (no description set)
Changed local branches:
branch-1:
+ ulyvmwyz 1d843d1f branch-1 | Commit 1
bookmark-1:
+ ulyvmwyz 1d843d1f bookmark-1 | Commit 1
- (absent)
Changed remote branches:
branch-1@origin:
+ tracked ulyvmwyz 1d843d1f branch-1 | Commit 1
- untracked ulyvmwyz 1d843d1f branch-1 | Commit 1
bookmark-1@origin:
+ tracked ulyvmwyz 1d843d1f bookmark-1 | Commit 1
- untracked ulyvmwyz 1d843d1f bookmark-1 | Commit 1
"###);
let stdout_without_from_to = test_env.jj_cmd_success(&repo_path, &["op", "diff"]);
assert_eq!(stdout, stdout_without_from_to);
@ -762,41 +762,41 @@ fn test_op_diff() {
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff", "--from", "0000000"]);
insta::assert_snapshot!(&stdout, @r###"
From operation 000000000000: root()
To operation 984d5ceb039f: check out git remote's default branch
To operation ea112f6a02be: check out git remote's default branch
Changed commits:
Change sqpuoqvxutmz
+ sqpuoqvx 9708515f (empty) (no description set)
Change ulyvmwyzwuwt
+ ulyvmwyz 1d843d1f branch-1 | Commit 1
+ ulyvmwyz 1d843d1f bookmark-1 | Commit 1
Change tqyxmsztkvot
+ tqyxmszt 3e785984 branch-3@origin | Commit 3
+ tqyxmszt 3e785984 bookmark-3@origin | Commit 3
Change yuvsmzqkmpws
+ yuvsmzqk 3d9189bc branch-2@origin | Commit 2
+ yuvsmzqk 3d9189bc bookmark-2@origin | Commit 2
Change zzzzzzzzzzzz
+ zzzzzzzz 00000000 (empty) (no description set)
Changed local branches:
branch-1:
+ ulyvmwyz 1d843d1f branch-1 | Commit 1
bookmark-1:
+ ulyvmwyz 1d843d1f bookmark-1 | Commit 1
- (absent)
Changed remote branches:
branch-1@origin:
+ tracked ulyvmwyz 1d843d1f branch-1 | Commit 1
bookmark-1@origin:
+ tracked ulyvmwyz 1d843d1f bookmark-1 | Commit 1
- untracked (absent)
branch-2@origin:
+ untracked yuvsmzqk 3d9189bc branch-2@origin | Commit 2
bookmark-2@origin:
+ untracked yuvsmzqk 3d9189bc bookmark-2@origin | Commit 2
- untracked (absent)
branch-3@origin:
+ untracked tqyxmszt 3e785984 branch-3@origin | Commit 3
bookmark-3@origin:
+ untracked tqyxmszt 3e785984 bookmark-3@origin | Commit 3
- untracked (absent)
"###);
// Diff from latest operation to root operation
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff", "--to", "0000000"]);
insta::assert_snapshot!(&stdout, @r###"
From operation 984d5ceb039f: check out git remote's default branch
From operation ea112f6a02be: check out git remote's default branch
To operation 000000000000: root()
Changed commits:
@ -812,31 +812,31 @@ fn test_op_diff() {
- zzzzzzzz hidden 00000000 (empty) (no description set)
Changed local branches:
branch-1:
bookmark-1:
+ (absent)
- ulyvmwyz hidden 1d843d1f Commit 1
Changed remote branches:
branch-1@origin:
bookmark-1@origin:
+ untracked (absent)
- tracked ulyvmwyz hidden 1d843d1f Commit 1
branch-2@origin:
bookmark-2@origin:
+ untracked (absent)
- untracked yuvsmzqk hidden 3d9189bc Commit 2
branch-3@origin:
bookmark-3@origin:
+ untracked (absent)
- untracked tqyxmszt hidden 3e785984 Commit 3
"###);
// Create a conflicted branch using a concurrent operation.
// Create a conflicted bookmark using a concurrent operation.
test_env.jj_cmd_ok(
&repo_path,
&[
"branch",
"bookmark",
"set",
"branch-1",
"bookmark-1",
"-r",
"branch-2@origin",
"bookmark-2@origin",
"--at-op",
"@-",
],
@ -847,16 +847,16 @@ fn test_op_diff() {
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]);
insta::assert_snapshot!(&stdout, @r###"
@ afab0949fddc test-username@host.example.com 2001-02-03 04:05:16.000 +07:00 - 2001-02-03 04:05:16.000 +07:00
@ f534dfc3151b test-username@host.example.com 2001-02-03 04:05:16.000 +07:00 - 2001-02-03 04:05:16.000 +07:00
reconcile divergent operations
args: jj log
984d5ceb039f test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
ea112f6a02be test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
check out git remote's default branch
args: jj git clone git-repo repo
5ed581429582 test-username@host.example.com 2001-02-03 04:05:15.000 +07:00 - 2001-02-03 04:05:15.000 +07:00
point branch branch-1 to commit 3d9189bc56a1972729350456eb95ec5bf90be2a8
args: jj branch set branch-1 -r branch-2@origin --at-op @-
817baaeefcbb test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
9ded7e2755e1 test-username@host.example.com 2001-02-03 04:05:15.000 +07:00 - 2001-02-03 04:05:15.000 +07:00
point bookmark bookmark-1 to commit 3d9189bc56a1972729350456eb95ec5bf90be2a8
args: jj bookmark set bookmark-1 -r bookmark-2@origin --at-op @-
cba9d7096849 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
fetch from git remote into empty repo
args: jj git clone git-repo repo
b51416386f26 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
@ -876,14 +876,14 @@ fn test_op_diff() {
&["op", "diff", "--from", first_parent_id, "--to", op_id],
);
insta::assert_snapshot!(&stdout, @r###"
From operation 984d5ceb039f: check out git remote's default branch
To operation afab0949fddc: reconcile divergent operations
From operation ea112f6a02be: check out git remote's default branch
To operation f534dfc3151b: reconcile divergent operations
Changed local branches:
branch-1:
+ (added) ulyvmwyz 1d843d1f branch-1?? branch-1@origin | Commit 1
+ (added) yuvsmzqk 3d9189bc branch-1?? branch-2@origin | Commit 2
- ulyvmwyz 1d843d1f branch-1?? branch-1@origin | Commit 1
bookmark-1:
+ (added) ulyvmwyz 1d843d1f bookmark-1?? bookmark-1@origin | Commit 1
+ (added) yuvsmzqk 3d9189bc bookmark-1?? bookmark-2@origin | Commit 2
- ulyvmwyz 1d843d1f bookmark-1?? bookmark-1@origin | Commit 1
"###);
// Diff between the second parent of the merge operation and the merge
@ -893,8 +893,8 @@ fn test_op_diff() {
&["op", "diff", "--from", second_parent_id, "--to", op_id],
);
insta::assert_snapshot!(&stdout, @r###"
From operation 5ed581429582: point branch branch-1 to commit 3d9189bc56a1972729350456eb95ec5bf90be2a8
To operation afab0949fddc: reconcile divergent operations
From operation 9ded7e2755e1: point bookmark bookmark-1 to commit 3d9189bc56a1972729350456eb95ec5bf90be2a8
To operation f534dfc3151b: reconcile divergent operations
Changed commits:
Change sqpuoqvxutmz
@ -903,15 +903,15 @@ fn test_op_diff() {
- qpvuntsm hidden 230dd059 (empty) (no description set)
Changed local branches:
branch-1:
+ (added) ulyvmwyz 1d843d1f branch-1?? branch-1@origin | Commit 1
+ (added) yuvsmzqk 3d9189bc branch-1?? branch-2@origin | Commit 2
- yuvsmzqk 3d9189bc branch-1?? branch-2@origin | Commit 2
bookmark-1:
+ (added) ulyvmwyz 1d843d1f bookmark-1?? bookmark-1@origin | Commit 1
+ (added) yuvsmzqk 3d9189bc bookmark-1?? bookmark-2@origin | Commit 2
- yuvsmzqk 3d9189bc bookmark-1?? bookmark-2@origin | Commit 2
Changed remote branches:
branch-1@origin:
+ tracked ulyvmwyz 1d843d1f branch-1?? branch-1@origin | Commit 1
- untracked ulyvmwyz 1d843d1f branch-1?? branch-1@origin | Commit 1
bookmark-1@origin:
+ tracked ulyvmwyz 1d843d1f bookmark-1?? bookmark-1@origin | Commit 1
- untracked ulyvmwyz 1d843d1f bookmark-1?? bookmark-1@origin | Commit 1
"###);
// Test fetching from git remote.
@ -920,140 +920,170 @@ fn test_op_diff() {
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
branch: branch-1@origin [updated] tracked
branch: branch-2@origin [updated] untracked
branch: branch-3@origin [deleted] untracked
branch: bookmark-1@origin [updated] tracked
branch: bookmark-2@origin [updated] untracked
branch: bookmark-3@origin [deleted] untracked
Abandoned 1 commits that are no longer reachable.
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff"]);
insta::assert_snapshot!(&stdout, @r###"
From operation afab0949fddc: reconcile divergent operations
To operation aa3c1cbed385: fetch from git remote(s) origin
From operation f534dfc3151b: reconcile divergent operations
To operation 8a21e297a587: fetch from git remote(s) origin
Changed commits:
Change qzxslznxxpoz
+ qzxslznx d487febd branch-2@origin | Commit 5
+ qzxslznx d487febd bookmark-2@origin | Commit 5
Change slvtnnzxztqy
+ slvtnnzx 4f856199 branch-1?? branch-1@origin | Commit 4
+ slvtnnzx 4f856199 bookmark-1?? bookmark-1@origin | Commit 4
Change tqyxmsztkvot
- tqyxmszt hidden 3e785984 Commit 3
Changed local branches:
branch-1:
+ (added) slvtnnzx 4f856199 branch-1?? branch-1@origin | Commit 4
+ (added) yuvsmzqk 3d9189bc branch-1?? | Commit 2
bookmark-1:
+ (added) slvtnnzx 4f856199 bookmark-1?? bookmark-1@origin | Commit 4
+ (added) yuvsmzqk 3d9189bc bookmark-1?? | Commit 2
- (added) ulyvmwyz 1d843d1f Commit 1
- (added) yuvsmzqk 3d9189bc branch-1?? | Commit 2
- (added) yuvsmzqk 3d9189bc bookmark-1?? | Commit 2
Changed remote branches:
branch-1@origin:
+ tracked slvtnnzx 4f856199 branch-1?? branch-1@origin | Commit 4
bookmark-1@origin:
+ tracked slvtnnzx 4f856199 bookmark-1?? bookmark-1@origin | Commit 4
- tracked ulyvmwyz 1d843d1f Commit 1
branch-2@origin:
+ untracked qzxslznx d487febd branch-2@origin | Commit 5
- untracked yuvsmzqk 3d9189bc branch-1?? | Commit 2
branch-3@origin:
bookmark-2@origin:
+ untracked qzxslznx d487febd bookmark-2@origin | Commit 5
- untracked yuvsmzqk 3d9189bc bookmark-1?? | Commit 2
bookmark-3@origin:
+ untracked (absent)
- untracked tqyxmszt hidden 3e785984 Commit 3
"###);
// Test creation of branch.
// Test creation of bookmark.
let (stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
&["branch", "create", "branch-2", "-r", "branch-2@origin"],
&[
"bookmark",
"create",
"bookmark-2",
"-r",
"bookmark-2@origin",
],
);
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
Created 1 branches pointing to qzxslznx d487febd branch-2 branch-2@origin | Commit 5
Created 1 bookmarks pointing to qzxslznx d487febd bookmark-2 bookmark-2@origin | Commit 5
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff"]);
insta::assert_snapshot!(&stdout, @r###"
From operation aa3c1cbed385: fetch from git remote(s) origin
To operation be49cc959876: create branch branch-2 pointing to commit d487febd08e690ee775a4e0387e30d544307e409
From operation 8a21e297a587: fetch from git remote(s) origin
To operation ef314062f7f5: create bookmark bookmark-2 pointing to commit d487febd08e690ee775a4e0387e30d544307e409
Changed local branches:
branch-2:
+ qzxslznx d487febd branch-2 branch-2@origin | Commit 5
bookmark-2:
+ qzxslznx d487febd bookmark-2 bookmark-2@origin | Commit 5
- (absent)
"###);
// Test tracking of branch.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "track", "branch-2@origin"]);
// Test tracking of bookmark.
let (stdout, stderr) =
test_env.jj_cmd_ok(&repo_path, &["bookmark", "track", "bookmark-2@origin"]);
insta::assert_snapshot!(&stdout, @r###"
"###);
"###);
insta::assert_snapshot!(&stderr, @r###"
Started tracking 1 remote branches.
Started tracking 1 remote bookmarks.
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff"]);
insta::assert_snapshot!(&stdout, @r###"
From operation be49cc959876: create branch branch-2 pointing to commit d487febd08e690ee775a4e0387e30d544307e409
To operation c6bace1690a5: track remote branch branch-2@origin
From operation ef314062f7f5: create bookmark bookmark-2 pointing to commit d487febd08e690ee775a4e0387e30d544307e409
To operation bc306c9bb67f: track remote bookmark bookmark-2@origin
Changed remote branches:
branch-2@origin:
+ tracked qzxslznx d487febd branch-2 | Commit 5
- untracked qzxslznx d487febd branch-2 | Commit 5
bookmark-2@origin:
+ tracked qzxslznx d487febd bookmark-2 | Commit 5
- untracked qzxslznx d487febd bookmark-2 | Commit 5
"###);
// Test creation of new commit.
// Test tracking of bookmark.
let (stdout, stderr) =
test_env.jj_cmd_ok(&repo_path, &["new", "branch-1@origin", "-m", "new commit"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "track", "bookmark-2@origin"]);
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
Working copy now at: nmzmmopx bed2698f (empty) new commit
Parent commit : slvtnnzx 4f856199 branch-1?? branch-1@origin | Commit 4
Warning: Remote bookmark already tracked: bookmark-2@origin
Nothing changed.
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff"]);
insta::assert_snapshot!(&stdout, @r###"
From operation ef314062f7f5: create bookmark bookmark-2 pointing to commit d487febd08e690ee775a4e0387e30d544307e409
To operation bc306c9bb67f: track remote bookmark bookmark-2@origin
Changed remote branches:
bookmark-2@origin:
+ tracked qzxslznx d487febd bookmark-2 | Commit 5
- untracked qzxslznx d487febd bookmark-2 | Commit 5
"###);
// Test creation of new commit.
let (stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
&["new", "bookmark-1@origin", "-m", "new commit"],
);
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
Working copy now at: wvuyspvk 358b82d6 (empty) new commit
Parent commit : slvtnnzx 4f856199 bookmark-1?? bookmark-1@origin | Commit 4
Added 1 files, modified 0 files, removed 1 files
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff"]);
insta::assert_snapshot!(&stdout, @r###"
From operation c6bace1690a5: track remote branch branch-2@origin
To operation 06ad17cad045: new empty commit
From operation bc306c9bb67f: track remote bookmark bookmark-2@origin
To operation 1ae777a7acc8: new empty commit
Changed commits:
Change nmzmmopxokps
+ nmzmmopx bed2698f (empty) new commit
Change wvuyspvkupzz
+ wvuyspvk 358b82d6 (empty) new commit
Change sqpuoqvxutmz
- sqpuoqvx hidden 9708515f (empty) (no description set)
"###);
// Test updating of local branch.
// Test updating of local bookmark.
let (stdout, stderr) =
test_env.jj_cmd_ok(&repo_path, &["branch", "set", "branch-1", "-r", "@"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "set", "bookmark-1", "-r", "@"]);
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
Moved 1 branches to nmzmmopx bed2698f branch-1* | (empty) new commit
Moved 1 bookmarks to wvuyspvk 358b82d6 bookmark-1* | (empty) new commit
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff"]);
insta::assert_snapshot!(&stdout, @r###"
From operation 06ad17cad045: new empty commit
To operation f98d2aca274d: point branch branch-1 to commit bed2698f6baf06f7eea56c616bc3fe36d9065651
From operation 1ae777a7acc8: new empty commit
To operation 5728693d7de3: point bookmark bookmark-1 to commit 358b82d6be53fa9b062325abb8bc820a8b34c68d
Changed local branches:
branch-1:
+ nmzmmopx bed2698f branch-1* | (empty) new commit
- (added) slvtnnzx 4f856199 branch-1@origin | Commit 4
bookmark-1:
+ wvuyspvk 358b82d6 bookmark-1* | (empty) new commit
- (added) slvtnnzx 4f856199 bookmark-1@origin | Commit 4
- (added) yuvsmzqk 3d9189bc Commit 2
"###);
// Test deletion of local branch.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "delete", "branch-2"]);
// Test deletion of local bookmark.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["bookmark", "delete", "bookmark-2"]);
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
Deleted 1 branches.
Deleted 1 bookmarks.
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff"]);
insta::assert_snapshot!(&stdout, @r###"
From operation f98d2aca274d: point branch branch-1 to commit bed2698f6baf06f7eea56c616bc3fe36d9065651
To operation 238af436e327: delete branch branch-2
From operation 5728693d7de3: point bookmark bookmark-1 to commit 358b82d6be53fa9b062325abb8bc820a8b34c68d
To operation 0f77d601f1cd: delete bookmark bookmark-2
Changed local branches:
branch-2:
bookmark-2:
+ (absent)
- qzxslznx d487febd branch-2@origin | Commit 5
- qzxslznx d487febd bookmark-2@origin | Commit 5
"###);
// Test pushing to Git remote.
@ -1062,26 +1092,26 @@ fn test_op_diff() {
"###);
insta::assert_snapshot!(&stderr, @r###"
Branch changes to push to origin:
Move forward branch branch-1 from 4f856199edbf to bed2698f6baf
Delete branch branch-2 from d487febd08e6
Move forward bookmark bookmark-1 from 4f856199edbf to 358b82d6be53
Delete bookmark bookmark-2 from d487febd08e6
Warning: The working-copy commit in workspace 'default' became immutable, so a new commit has been created on top of it.
Working copy now at: uuuvxpvw 2c8e84a8 (empty) (no description set)
Parent commit : nmzmmopx bed2698f branch-1 | (empty) new commit
Working copy now at: oupztwtk 2f0718a0 (empty) (no description set)
Parent commit : wvuyspvk 358b82d6 bookmark-1 | (empty) new commit
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "diff"]);
insta::assert_snapshot!(&stdout, @r###"
From operation 238af436e327: delete branch branch-2
To operation c5aa1d1304a6: push all tracked branches to git remote origin
From operation 0f77d601f1cd: delete bookmark bookmark-2
To operation 65409e59d36a: push all tracked bookmarks to git remote origin
Changed commits:
Change uuuvxpvwspwr
+ uuuvxpvw 2c8e84a8 (empty) (no description set)
Change oupztwtkortx
+ oupztwtk 2f0718a0 (empty) (no description set)
Changed remote branches:
branch-1@origin:
+ tracked nmzmmopx bed2698f branch-1 | (empty) new commit
bookmark-1@origin:
+ tracked wvuyspvk 358b82d6 bookmark-1 | (empty) new commit
- tracked slvtnnzx 4f856199 Commit 4
branch-2@origin:
bookmark-2@origin:
+ untracked (absent)
- tracked qzxslznx d487febd Commit 5
"###);
@ -1326,10 +1356,10 @@ fn test_op_show() {
// Overview of op log.
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]);
insta::assert_snapshot!(&stdout, @r###"
@ 984d5ceb039f test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
@ ea112f6a02be test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
check out git remote's default branch
args: jj git clone git-repo repo
817baaeefcbb test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
cba9d7096849 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
fetch from git remote into empty repo
args: jj git clone git-repo repo
b51416386f26 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
@ -1348,7 +1378,7 @@ fn test_op_show() {
// Showing the latest operation.
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "show", "@"]);
insta::assert_snapshot!(&stdout, @r###"
984d5ceb039f test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
ea112f6a02be test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
check out git remote's default branch
args: jj git clone git-repo repo
@ -1359,14 +1389,14 @@ fn test_op_show() {
- qpvuntsm hidden 230dd059 (empty) (no description set)
Changed local branches:
branch-1:
+ ulyvmwyz 1d843d1f branch-1 | Commit 1
bookmark-1:
+ ulyvmwyz 1d843d1f bookmark-1 | Commit 1
- (absent)
Changed remote branches:
branch-1@origin:
+ tracked ulyvmwyz 1d843d1f branch-1 | Commit 1
- untracked ulyvmwyz 1d843d1f branch-1 | Commit 1
bookmark-1@origin:
+ tracked ulyvmwyz 1d843d1f bookmark-1 | Commit 1
- untracked ulyvmwyz 1d843d1f bookmark-1 | Commit 1
"###);
// `jj op show @` should behave identically to `jj op show`.
let stdout_without_op_id = test_env.jj_cmd_success(&repo_path, &["op", "show"]);
@ -1375,39 +1405,39 @@ fn test_op_show() {
// Showing a given operation.
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "show", "@-"]);
insta::assert_snapshot!(&stdout, @r###"
817baaeefcbb test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
cba9d7096849 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
fetch from git remote into empty repo
args: jj git clone git-repo repo
Changed commits:
Change tqyxmsztkvot
+ tqyxmszt 3e785984 branch-3@origin | Commit 3
+ tqyxmszt 3e785984 bookmark-3@origin | Commit 3
Change yuvsmzqkmpws
+ yuvsmzqk 3d9189bc branch-2@origin | Commit 2
+ yuvsmzqk 3d9189bc bookmark-2@origin | Commit 2
Change ulyvmwyzwuwt
+ ulyvmwyz 1d843d1f branch-1@origin | Commit 1
+ ulyvmwyz 1d843d1f bookmark-1@origin | Commit 1
Changed remote branches:
branch-1@origin:
+ untracked ulyvmwyz 1d843d1f branch-1@origin | Commit 1
bookmark-1@origin:
+ untracked ulyvmwyz 1d843d1f bookmark-1@origin | Commit 1
- untracked (absent)
branch-2@origin:
+ untracked yuvsmzqk 3d9189bc branch-2@origin | Commit 2
bookmark-2@origin:
+ untracked yuvsmzqk 3d9189bc bookmark-2@origin | Commit 2
- untracked (absent)
branch-3@origin:
+ untracked tqyxmszt 3e785984 branch-3@origin | Commit 3
bookmark-3@origin:
+ untracked tqyxmszt 3e785984 bookmark-3@origin | Commit 3
- untracked (absent)
"###);
// Create a conflicted branch using a concurrent operation.
// Create a conflicted bookmark using a concurrent operation.
test_env.jj_cmd_ok(
&repo_path,
&[
"branch",
"bookmark",
"set",
"branch-1",
"bookmark-1",
"-r",
"branch-2@origin",
"bookmark-2@origin",
"--at-op",
"@-",
],
@ -1419,7 +1449,7 @@ fn test_op_show() {
// Showing a merge operation is empty.
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "show"]);
insta::assert_snapshot!(&stdout, @r###"
40495fbd29b6 test-username@host.example.com 2001-02-03 04:05:14.000 +07:00 - 2001-02-03 04:05:14.000 +07:00
5d52ce7672bb test-username@host.example.com 2001-02-03 04:05:14.000 +07:00 - 2001-02-03 04:05:14.000 +07:00
reconcile divergent operations
args: jj log
"###);
@ -1430,146 +1460,176 @@ fn test_op_show() {
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
branch: branch-1@origin [updated] tracked
branch: branch-2@origin [updated] untracked
branch: branch-3@origin [deleted] untracked
branch: bookmark-1@origin [updated] tracked
branch: bookmark-2@origin [updated] untracked
branch: bookmark-3@origin [deleted] untracked
Abandoned 1 commits that are no longer reachable.
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "show"]);
insta::assert_snapshot!(&stdout, @r###"
1ed37acf8335 test-username@host.example.com 2001-02-03 04:05:16.000 +07:00 - 2001-02-03 04:05:16.000 +07:00
5eed0f5f5fbc test-username@host.example.com 2001-02-03 04:05:16.000 +07:00 - 2001-02-03 04:05:16.000 +07:00
fetch from git remote(s) origin
args: jj git fetch
Changed commits:
Change qzxslznxxpoz
+ qzxslznx d487febd branch-2@origin | Commit 5
+ qzxslznx d487febd bookmark-2@origin | Commit 5
Change slvtnnzxztqy
+ slvtnnzx 4f856199 branch-1?? branch-1@origin | Commit 4
+ slvtnnzx 4f856199 bookmark-1?? bookmark-1@origin | Commit 4
Change tqyxmsztkvot
- tqyxmszt hidden 3e785984 Commit 3
Changed local branches:
branch-1:
+ (added) slvtnnzx 4f856199 branch-1?? branch-1@origin | Commit 4
+ (added) yuvsmzqk 3d9189bc branch-1?? | Commit 2
bookmark-1:
+ (added) slvtnnzx 4f856199 bookmark-1?? bookmark-1@origin | Commit 4
+ (added) yuvsmzqk 3d9189bc bookmark-1?? | Commit 2
- (added) ulyvmwyz 1d843d1f Commit 1
- (added) yuvsmzqk 3d9189bc branch-1?? | Commit 2
- (added) yuvsmzqk 3d9189bc bookmark-1?? | Commit 2
Changed remote branches:
branch-1@origin:
+ tracked slvtnnzx 4f856199 branch-1?? branch-1@origin | Commit 4
bookmark-1@origin:
+ tracked slvtnnzx 4f856199 bookmark-1?? bookmark-1@origin | Commit 4
- tracked ulyvmwyz 1d843d1f Commit 1
branch-2@origin:
+ untracked qzxslznx d487febd branch-2@origin | Commit 5
- untracked yuvsmzqk 3d9189bc branch-1?? | Commit 2
branch-3@origin:
bookmark-2@origin:
+ untracked qzxslznx d487febd bookmark-2@origin | Commit 5
- untracked yuvsmzqk 3d9189bc bookmark-1?? | Commit 2
bookmark-3@origin:
+ untracked (absent)
- untracked tqyxmszt hidden 3e785984 Commit 3
"###);
// Test creation of branch.
// Test creation of bookmark.
let (stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
&["branch", "create", "branch-2", "-r", "branch-2@origin"],
&[
"bookmark",
"create",
"bookmark-2",
"-r",
"bookmark-2@origin",
],
);
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
Created 1 branches pointing to qzxslznx d487febd branch-2 branch-2@origin | Commit 5
Created 1 bookmarks pointing to qzxslznx d487febd bookmark-2 bookmark-2@origin | Commit 5
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "show"]);
insta::assert_snapshot!(&stdout, @r###"
61235941fe15 test-username@host.example.com 2001-02-03 04:05:18.000 +07:00 - 2001-02-03 04:05:18.000 +07:00
create branch branch-2 pointing to commit d487febd08e690ee775a4e0387e30d544307e409
args: jj branch create branch-2 -r branch-2@origin
25b687bb01b6 test-username@host.example.com 2001-02-03 04:05:18.000 +07:00 - 2001-02-03 04:05:18.000 +07:00
create bookmark bookmark-2 pointing to commit d487febd08e690ee775a4e0387e30d544307e409
args: jj bookmark create bookmark-2 -r bookmark-2@origin
Changed local branches:
branch-2:
+ qzxslznx d487febd branch-2 branch-2@origin | Commit 5
bookmark-2:
+ qzxslznx d487febd bookmark-2 bookmark-2@origin | Commit 5
- (absent)
"###);
// Test tracking of branch.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "track", "branch-2@origin"]);
// Test tracking of a bookmark.
let (stdout, stderr) =
test_env.jj_cmd_ok(&repo_path, &["bookmark", "track", "bookmark-2@origin"]);
insta::assert_snapshot!(&stdout, @r###"
"###);
"###);
insta::assert_snapshot!(&stderr, @r###"
Started tracking 1 remote branches.
Started tracking 1 remote bookmarks.
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "show"]);
insta::assert_snapshot!(&stdout, @r###"
18035ff599c1 test-username@host.example.com 2001-02-03 04:05:20.000 +07:00 - 2001-02-03 04:05:20.000 +07:00
track remote branch branch-2@origin
args: jj branch track branch-2@origin
48cf434c463d test-username@host.example.com 2001-02-03 04:05:20.000 +07:00 - 2001-02-03 04:05:20.000 +07:00
track remote bookmark bookmark-2@origin
args: jj bookmark track bookmark-2@origin
Changed remote branches:
branch-2@origin:
+ tracked qzxslznx d487febd branch-2 | Commit 5
- untracked qzxslznx d487febd branch-2 | Commit 5
bookmark-2@origin:
+ tracked qzxslznx d487febd bookmark-2 | Commit 5
- untracked qzxslznx d487febd bookmark-2 | Commit 5
"###);
// Test creation of new commit.
let (stdout, stderr) =
test_env.jj_cmd_ok(&repo_path, &["new", "branch-1@origin", "-m", "new commit"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "track", "bookmark-2@origin"]);
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
Working copy now at: nkmrtpmo 71fe694d (empty) new commit
Parent commit : slvtnnzx 4f856199 branch-1?? branch-1@origin | Commit 4
Warning: Remote bookmark already tracked: bookmark-2@origin
Nothing changed.
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "show"]);
insta::assert_snapshot!(&stdout, @r###"
48cf434c463d test-username@host.example.com 2001-02-03 04:05:20.000 +07:00 - 2001-02-03 04:05:20.000 +07:00
track remote bookmark bookmark-2@origin
args: jj bookmark track bookmark-2@origin
Changed remote branches:
bookmark-2@origin:
+ tracked qzxslznx d487febd bookmark-2 | Commit 5
- untracked qzxslznx d487febd bookmark-2 | Commit 5
"###);
// Test creation of new commit.
let (stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
&["new", "bookmark-1@origin", "-m", "new commit"],
);
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
Working copy now at: xznxytkn eb6c2b21 (empty) new commit
Parent commit : slvtnnzx 4f856199 bookmark-1?? bookmark-1@origin | Commit 4
Added 1 files, modified 0 files, removed 1 files
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "show"]);
insta::assert_snapshot!(&stdout, @r###"
6ae7eab9f2c5 test-username@host.example.com 2001-02-03 04:05:22.000 +07:00 - 2001-02-03 04:05:22.000 +07:00
89aa6cca51fc test-username@host.example.com 2001-02-03 04:05:24.000 +07:00 - 2001-02-03 04:05:24.000 +07:00
new empty commit
args: jj new branch-1@origin -m 'new commit'
args: jj new bookmark-1@origin -m 'new commit'
Changed commits:
Change nkmrtpmomlro
+ nkmrtpmo 71fe694d (empty) new commit
Change xznxytknoqwo
+ xznxytkn eb6c2b21 (empty) new commit
Change sqpuoqvxutmz
- sqpuoqvx hidden 9708515f (empty) (no description set)
"###);
// Test updating of local branch.
// Test updating of local bookmark.
let (stdout, stderr) =
test_env.jj_cmd_ok(&repo_path, &["branch", "set", "branch-1", "-r", "@"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "set", "bookmark-1", "-r", "@"]);
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
Moved 1 branches to nkmrtpmo 71fe694d branch-1* | (empty) new commit
Moved 1 bookmarks to xznxytkn eb6c2b21 bookmark-1* | (empty) new commit
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "show"]);
insta::assert_snapshot!(&stdout, @r###"
044836a4ea77 test-username@host.example.com 2001-02-03 04:05:24.000 +07:00 - 2001-02-03 04:05:24.000 +07:00
point branch branch-1 to commit 71fe694da7811a184f404fffe35cd62b0adb3d89
args: jj branch set branch-1 -r @
6d6bf9b35d8a test-username@host.example.com 2001-02-03 04:05:26.000 +07:00 - 2001-02-03 04:05:26.000 +07:00
point bookmark bookmark-1 to commit eb6c2b21ec20a33ab6a1c44bc86c59d84ffd93ac
args: jj bookmark set bookmark-1 -r @
Changed local branches:
branch-1:
+ nkmrtpmo 71fe694d branch-1* | (empty) new commit
- (added) slvtnnzx 4f856199 branch-1@origin | Commit 4
bookmark-1:
+ xznxytkn eb6c2b21 bookmark-1* | (empty) new commit
- (added) slvtnnzx 4f856199 bookmark-1@origin | Commit 4
- (added) yuvsmzqk 3d9189bc Commit 2
"###);
// Test deletion of local branch.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "delete", "branch-2"]);
// Test deletion of local bookmark.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["bookmark", "delete", "bookmark-2"]);
insta::assert_snapshot!(&stdout, @r###"
"###);
insta::assert_snapshot!(&stderr, @r###"
Deleted 1 branches.
Deleted 1 bookmarks.
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "show"]);
insta::assert_snapshot!(&stdout, @r###"
6595a222e6f1 test-username@host.example.com 2001-02-03 04:05:26.000 +07:00 - 2001-02-03 04:05:26.000 +07:00
delete branch branch-2
args: jj branch delete branch-2
a50c95a6b180 test-username@host.example.com 2001-02-03 04:05:28.000 +07:00 - 2001-02-03 04:05:28.000 +07:00
delete bookmark bookmark-2
args: jj bookmark delete bookmark-2
Changed local branches:
branch-2:
bookmark-2:
+ (absent)
- qzxslznx d487febd branch-2@origin | Commit 5
- qzxslznx d487febd bookmark-2@origin | Commit 5
"###);
// Test pushing to Git remote.
@ -1578,27 +1638,27 @@ fn test_op_show() {
"###);
insta::assert_snapshot!(&stderr, @r###"
Branch changes to push to origin:
Move forward branch branch-1 from 4f856199edbf to 71fe694da781
Delete branch branch-2 from d487febd08e6
Move forward bookmark bookmark-1 from 4f856199edbf to eb6c2b21ec20
Delete bookmark bookmark-2 from d487febd08e6
Warning: The working-copy commit in workspace 'default' became immutable, so a new commit has been created on top of it.
Working copy now at: wvuyspvk 6136f89a (empty) (no description set)
Parent commit : nkmrtpmo 71fe694d branch-1 | (empty) new commit
Working copy now at: pzsxstzt 7ab2d837 (empty) (no description set)
Parent commit : xznxytkn eb6c2b21 bookmark-1 | (empty) new commit
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "show"]);
insta::assert_snapshot!(&stdout, @r###"
c69699290f76 test-username@host.example.com 2001-02-03 04:05:28.000 +07:00 - 2001-02-03 04:05:28.000 +07:00
push all tracked branches to git remote origin
5d994bb7d230 test-username@host.example.com 2001-02-03 04:05:30.000 +07:00 - 2001-02-03 04:05:30.000 +07:00
push all tracked bookmarks to git remote origin
args: jj git push --tracked
Changed commits:
Change wvuyspvkupzz
+ wvuyspvk 6136f89a (empty) (no description set)
Change pzsxstztnpkv
+ pzsxstzt 7ab2d837 (empty) (no description set)
Changed remote branches:
branch-1@origin:
+ tracked nkmrtpmo 71fe694d branch-1 | (empty) new commit
bookmark-1@origin:
+ tracked xznxytkn eb6c2b21 bookmark-1 | (empty) new commit
- tracked slvtnnzx 4f856199 Commit 4
branch-2@origin:
bookmark-2@origin:
+ untracked (absent)
- tracked qzxslznx d487febd Commit 5
"###);
@ -1725,7 +1785,7 @@ fn init_bare_git_repo(git_repo_path: &Path) -> git2::Repository {
.unwrap();
git_repo
.commit(
Some("refs/heads/branch-1"),
Some("refs/heads/bookmark-1"),
&git_signature,
&git_signature,
"Commit 1",
@ -1735,7 +1795,7 @@ fn init_bare_git_repo(git_repo_path: &Path) -> git2::Repository {
.unwrap();
git_repo
.commit(
Some("refs/heads/branch-2"),
Some("refs/heads/bookmark-2"),
&git_signature,
&git_signature,
"Commit 2",
@ -1745,7 +1805,7 @@ fn init_bare_git_repo(git_repo_path: &Path) -> git2::Repository {
.unwrap();
git_repo
.commit(
Some("refs/heads/branch-3"),
Some("refs/heads/bookmark-3"),
&git_signature,
&git_signature,
"Commit 3",
@ -1754,7 +1814,7 @@ fn init_bare_git_repo(git_repo_path: &Path) -> git2::Repository {
)
.unwrap();
drop(git_tree);
git_repo.set_head("refs/heads/branch-1").unwrap();
git_repo.set_head("refs/heads/bookmark-1").unwrap();
git_repo
}
@ -1773,44 +1833,44 @@ fn modify_git_repo(git_repo: git2::Repository) -> git2::Repository {
&git2::Time::new(123, 60),
)
.unwrap();
let branch1_commit = git_repo
.find_reference("refs/heads/branch-1")
let bookmark1_commit = git_repo
.find_reference("refs/heads/bookmark-1")
.unwrap()
.peel_to_commit()
.unwrap();
let branch2_commit = git_repo
.find_reference("refs/heads/branch-2")
let bookmark2_commit = git_repo
.find_reference("refs/heads/bookmark-2")
.unwrap()
.peel_to_commit()
.unwrap();
git_repo
.commit(
Some("refs/heads/branch-1"),
Some("refs/heads/bookmark-1"),
&git_signature,
&git_signature,
"Commit 4",
&git_tree,
&[&branch1_commit],
&[&bookmark1_commit],
)
.unwrap();
git_repo
.commit(
Some("refs/heads/branch-2"),
Some("refs/heads/bookmark-2"),
&git_signature,
&git_signature,
"Commit 5",
&git_tree,
&[&branch2_commit],
&[&bookmark2_commit],
)
.unwrap();
git_repo
.find_reference("refs/heads/branch-3")
.find_reference("refs/heads/bookmark-3")
.unwrap()
.delete()
.unwrap();
drop(git_tree);
drop(branch1_commit);
drop(branch2_commit);
drop(bookmark1_commit);
drop(bookmark2_commit);
git_repo
}

View file

@ -25,7 +25,7 @@ fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, paren
test_env.jj_cmd_ok(repo_path, &args);
}
std::fs::write(repo_path.join(name), format!("{name}\n")).unwrap();
test_env.jj_cmd_ok(repo_path, &["branch", "create", name]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", name]);
}
#[test]
@ -169,7 +169,7 @@ fn test_rebase_invalid() {
}
#[test]
fn test_rebase_branch() {
fn test_rebase_bookmark() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
@ -206,7 +206,7 @@ fn test_rebase_branch() {
"###);
// Test rebasing multiple branches at once
// Test rebasing multiple bookmarks at once
test_env.jj_cmd_ok(&repo_path, &["undo"]);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-b=e", "-b=d", "-d=b"]);
insta::assert_snapshot!(stdout, @"");
@ -260,7 +260,7 @@ fn test_rebase_branch() {
}
#[test]
fn test_rebase_branch_with_merge() {
fn test_rebase_bookmark_with_merge() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
@ -939,7 +939,7 @@ fn test_rebase_error_revision_does_not_exist() {
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "one"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b-one"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b-one"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-r", "@-", "-m", "two"]);
let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-b", "b-one", "-d", "this"]);
@ -1432,7 +1432,7 @@ fn test_rebase_revisions_after() {
"###);
test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
// Rebase a commit after a commit in a branch of a merge commit.
// Rebase a commit after a commit in a bookmark of a merge commit.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "f", "--after", "b1"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
@ -1459,7 +1459,7 @@ fn test_rebase_revisions_after() {
"###);
test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
// Rebase a commit after the last commit in a branch of a merge commit.
// Rebase a commit after the last commit in a bookmark of a merge commit.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "f", "--after", "b2"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
@ -1850,7 +1850,7 @@ fn test_rebase_revisions_before() {
"###);
test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
// Rebase a commit before a commit in a branch of a merge commit.
// Rebase a commit before a commit in a bookmark of a merge commit.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "f", "--before", "b2"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
@ -1877,7 +1877,7 @@ fn test_rebase_revisions_before() {
"###);
test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
// Rebase a commit before the first commit in a branch of a merge commit.
// Rebase a commit before the first commit in a bookmark of a merge commit.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "f", "--before", "b1"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
@ -1963,7 +1963,7 @@ fn test_rebase_revisions_before() {
"###);
test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
// Rebase a commit before two commits in separate branches to create a merge
// Rebase a commit before two commits in separate bookmarks to create a merge
// commit.
let (stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
@ -2455,12 +2455,12 @@ fn test_rebase_skip_if_on_destination() {
}
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
let template = "branches ++ surround(': ', '', parents.map(|c| c.branches()))";
let template = "bookmarks ++ surround(': ', '', parents.map(|c| c.bookmarks()))";
test_env.jj_cmd_success(repo_path, &["log", "-T", template])
}
fn get_long_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
let template = "branches ++ ' ' ++ change_id.shortest(8) ++ ' ' ++ commit_id.shortest(8) ++ \
surround(': ', '', parents.map(|c| c.branches()))";
let template = "bookmarks ++ ' ' ++ change_id.shortest(8) ++ ' ' ++ commit_id.shortest(8) \
++ surround(': ', '', parents.map(|c| c.bookmarks()))";
test_env.jj_cmd_success(repo_path, &["log", "-T", template])
}

View file

@ -35,11 +35,11 @@ fn create_commit(
for (name, content) in files {
std::fs::write(repo_path.join(name), content).unwrap();
}
test_env.jj_cmd_ok(repo_path, &["branch", "create", name]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", name]);
}
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["log", "-T", "branches"])
test_env.jj_cmd_success(repo_path, &["log", "-T", "bookmarks"])
}
#[test]

View file

@ -278,9 +278,9 @@ fn create_commit(
for (name, content) in files {
std::fs::write(repo_path.join(name), content).unwrap();
}
test_env.jj_cmd_ok(repo_path, &["branch", "create", name]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", name]);
}
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
test_env.jj_cmd_success(repo_path, &["log", "-T", "branches"])
test_env.jj_cmd_success(repo_path, &["log", "-T", "bookmarks"])
}

View file

@ -197,28 +197,28 @@ fn test_bad_function_call() {
4: Invalid component ".." in repo-relative path "../out"
"###);
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "branches(bad:pattern)"]);
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "bookmarks(bad:pattern)"]);
insta::assert_snapshot!(stderr, @r###"
Error: Failed to parse revset: Invalid string pattern
Caused by:
1: --> 1:10
1: --> 1:11
|
1 | branches(bad:pattern)
| ^---------^
1 | bookmarks(bad:pattern)
| ^---------^
|
= Invalid string pattern
2: Invalid string pattern kind "bad:"
Hint: Try prefixing with one of `exact:`, `glob:`, `regex:`, or `substring:`
"###);
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "branches(regex:'(')"]);
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "bookmarks(regex:'(')"]);
insta::assert_snapshot!(stderr, @r###"
Error: Failed to parse revset: Invalid string pattern
Caused by:
1: --> 1:10
1: --> 1:11
|
1 | branches(regex:'(')
| ^-------^
1 | bookmarks(regex:'(')
| ^-------^
|
= Invalid string pattern
2: regex parse error:
@ -240,48 +240,48 @@ fn test_bad_function_call() {
let stderr = test_env.jj_cmd_failure(
&repo_path,
&["log", "-r", "remote_branches(a, b, remote=c)"],
&["log", "-r", "remote_bookmarks(a, b, remote=c)"],
);
insta::assert_snapshot!(stderr, @r###"
Error: Failed to parse revset: Function "remote_branches": Got multiple values for keyword "remote"
Caused by: --> 1:23
Error: Failed to parse revset: Function "remote_bookmarks": Got multiple values for keyword "remote"
Caused by: --> 1:24
|
1 | remote_branches(a, b, remote=c)
| ^------^
1 | remote_bookmarks(a, b, remote=c)
| ^------^
|
= Function "remote_branches": Got multiple values for keyword "remote"
= Function "remote_bookmarks": Got multiple values for keyword "remote"
"###);
let stderr =
test_env.jj_cmd_failure(&repo_path, &["log", "-r", "remote_branches(remote=a, b)"]);
test_env.jj_cmd_failure(&repo_path, &["log", "-r", "remote_bookmarks(remote=a, b)"]);
insta::assert_snapshot!(stderr, @r###"
Error: Failed to parse revset: Function "remote_branches": Positional argument follows keyword argument
Caused by: --> 1:27
Error: Failed to parse revset: Function "remote_bookmarks": Positional argument follows keyword argument
Caused by: --> 1:28
|
1 | remote_branches(remote=a, b)
| ^
1 | remote_bookmarks(remote=a, b)
| ^
|
= Function "remote_branches": Positional argument follows keyword argument
= Function "remote_bookmarks": Positional argument follows keyword argument
"###);
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "remote_branches(=foo)"]);
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "remote_bookmarks(=foo)"]);
insta::assert_snapshot!(stderr, @r###"
Error: Failed to parse revset: Syntax error
Caused by: --> 1:17
Caused by: --> 1:18
|
1 | remote_branches(=foo)
| ^---
1 | remote_bookmarks(=foo)
| ^---
|
= expected <identifier> or <expression>
"###);
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "remote_branches(remote=)"]);
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "remote_bookmarks(remote=)"]);
insta::assert_snapshot!(stderr, @r###"
Error: Failed to parse revset: Syntax error
Caused by: --> 1:24
Caused by: --> 1:25
|
1 | remote_branches(remote=)
| ^---
1 | remote_bookmarks(remote=)
| ^---
|
= expected <expression>
"###);
@ -297,23 +297,23 @@ fn test_function_name_hint() {
test_env.add_config(
r###"
[revset-aliases]
'branches(x)' = 'x' # override builtin function
'bookmarks(x)' = 'x' # override builtin function
'my_author(x)' = 'author(x)' # similar name to builtin function
'author_sym' = 'x' # not a function alias
'my_branches' = 'branch()' # typo in alias
'my_bookmarks' = 'bookmark()' # typo in alias
"###,
);
// The suggestion "branches" shouldn't be duplicated
insta::assert_snapshot!(evaluate_err("branch()"), @r###"
Error: Failed to parse revset: Function "branch" doesn't exist
// The suggestion "bookmarks" shouldn't be duplicated
insta::assert_snapshot!(evaluate_err("bookmark()"), @r###"
Error: Failed to parse revset: Function "bookmark" doesn't exist
Caused by: --> 1:1
|
1 | branch()
| ^----^
1 | bookmark()
| ^------^
|
= Function "branch" doesn't exist
Hint: Did you mean "branches", "reachable"?
= Function "bookmark" doesn't exist
Hint: Did you mean "bookmarks", "remote_bookmarks"?
"###);
// Both builtin function and function alias should be suggested
@ -328,22 +328,22 @@ fn test_function_name_hint() {
Hint: Did you mean "author", "author_date", "my_author"?
"###);
insta::assert_snapshot!(evaluate_err("my_branches"), @r###"
Error: Failed to parse revset: Alias "my_branches" cannot be expanded
insta::assert_snapshot!(evaluate_err("my_bookmarks"), @r###"
Error: Failed to parse revset: Alias "my_bookmarks" cannot be expanded
Caused by:
1: --> 1:1
|
1 | my_branches
| ^---------^
1 | my_bookmarks
| ^----------^
|
= Alias "my_branches" cannot be expanded
= Alias "my_bookmarks" cannot be expanded
2: --> 1:1
|
1 | branch()
| ^----^
1 | bookmark()
| ^------^
|
= Function "branch" doesn't exist
Hint: Did you mean "branches", "reachable"?
= Function "bookmark" doesn't exist
Hint: Did you mean "bookmarks", "remote_bookmarks"?
"###);
}
@ -555,11 +555,11 @@ fn test_all_modifier() {
"###);
// Command that accepts only single revision
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "create", "-rall:@", "x"]);
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "-rall:@", "x"]);
insta::assert_snapshot!(stderr, @r###"
Created 1 branches pointing to qpvuntsm 230dd059 x | (empty) (no description set)
Created 1 bookmarks pointing to qpvuntsm 230dd059 x | (empty) (no description set)
"###);
let stderr = test_env.jj_cmd_failure(&repo_path, &["branch", "set", "-rall:all()", "x"]);
let stderr = test_env.jj_cmd_failure(&repo_path, &["bookmark", "set", "-rall:all()", "x"]);
insta::assert_snapshot!(stderr, @r###"
Error: Revset "all:all()" resolved to more than one revision
Hint: The revset "all:all()" resolved to these revisions:

View file

@ -18,7 +18,7 @@ use std::path::PathBuf;
use crate::common::TestEnvironment;
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
let template = r#"separate(" ", change_id.short(), empty, description, local_branches)"#;
let template = r#"separate(" ", change_id.short(), empty, description, local_bookmarks)"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}
@ -220,9 +220,9 @@ fn test_split_with_default_description() {
std::fs::write(workspace_path.join("file1"), "foo\n").unwrap();
std::fs::write(workspace_path.join("file2"), "bar\n").unwrap();
// Create a branch pointing to the commit. It will be moved to the second
// Create a bookmark pointing to the commit. It will be moved to the second
// commit after the split.
test_env.jj_cmd_ok(&workspace_path, &["branch", "create", "test_branch"]);
test_env.jj_cmd_ok(&workspace_path, &["bookmark", "create", "test_bookmark"]);
let edit_script = test_env.set_up_fake_editor();
std::fs::write(
@ -234,8 +234,8 @@ fn test_split_with_default_description() {
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
First part: qpvuntsm 48018df6 TESTED=TODO
Second part: kkmpptxz 350b4c13 test_branch | (no description set)
Working copy now at: kkmpptxz 350b4c13 test_branch | (no description set)
Second part: kkmpptxz 350b4c13 test_bookmark | (no description set)
Working copy now at: kkmpptxz 350b4c13 test_bookmark | (no description set)
Parent commit : qpvuntsm 48018df6 TESTED=TODO
"###);
@ -257,7 +257,7 @@ fn test_split_with_default_description() {
"###);
assert!(!test_env.env_root().join("editor2").exists());
insta::assert_snapshot!(get_log_output(&test_env, &workspace_path), @r###"
@ kkmpptxzrspx false test_branch
@ kkmpptxzrspx false test_bookmark
qpvuntsmwlqt false TESTED=TODO
zzzzzzzzzzzz true
"###);
@ -328,11 +328,11 @@ fn test_split_siblings_no_descendants() {
std::fs::write(workspace_path.join("file1"), "foo\n").unwrap();
std::fs::write(workspace_path.join("file2"), "bar\n").unwrap();
// Create a branch pointing to the commit. It will be moved to the second
// Create a bookmark pointing to the commit. It will be moved to the second
// commit after the split.
test_env.jj_cmd_ok(&workspace_path, &["branch", "create", "test_branch"]);
test_env.jj_cmd_ok(&workspace_path, &["bookmark", "create", "test_bookmark"]);
insta::assert_snapshot!(get_log_output(&test_env, &workspace_path), @r###"
@ qpvuntsmwlqt false test_branch
@ qpvuntsmwlqt false test_bookmark
zzzzzzzzzzzz true
"###);
@ -346,13 +346,13 @@ fn test_split_siblings_no_descendants() {
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
First part: qpvuntsm 0dced07a TESTED=TODO
Second part: zsuskuln 0473f014 test_branch | (no description set)
Working copy now at: zsuskuln 0473f014 test_branch | (no description set)
Second part: zsuskuln 0473f014 test_bookmark | (no description set)
Working copy now at: zsuskuln 0473f014 test_bookmark | (no description set)
Parent commit : zzzzzzzz 00000000 (empty) (no description set)
Added 0 files, modified 0 files, removed 1 files
"###);
insta::assert_snapshot!(get_log_output(&test_env, &workspace_path), @r###"
@ zsuskulnrvyr false test_branch
@ zsuskulnrvyr false test_bookmark
qpvuntsmwlqt false TESTED=TODO
zzzzzzzzzzzz true

View file

@ -23,13 +23,13 @@ fn test_squash() {
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
std::fs::write(repo_path.join("file1"), "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(repo_path.join("file1"), "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
std::fs::write(repo_path.join("file1"), "c\n").unwrap();
// Test the setup
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ -85,10 +85,10 @@ fn test_squash() {
test_env.jj_cmd_ok(&repo_path, &["undo"]);
test_env.jj_cmd_ok(&repo_path, &["edit", "b"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "d"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "d"]);
std::fs::write(repo_path.join("file2"), "d\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "c", "d"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "e"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "e"]);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ 41219719ab5f e (empty)
@ -137,15 +137,15 @@ fn test_squash_partial() {
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
std::fs::write(repo_path.join("file1"), "a\n").unwrap();
std::fs::write(repo_path.join("file2"), "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(repo_path.join("file1"), "b\n").unwrap();
std::fs::write(repo_path.join("file2"), "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
std::fs::write(repo_path.join("file1"), "c\n").unwrap();
std::fs::write(repo_path.join("file2"), "c\n").unwrap();
// Test the setup
@ -283,13 +283,13 @@ fn test_squash_keep_emptied() {
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
std::fs::write(repo_path.join("file1"), "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(repo_path.join("file1"), "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
std::fs::write(repo_path.join("file1"), "c\n").unwrap();
// Test the setup
@ -337,25 +337,25 @@ fn test_squash_from_to() {
//
// When moving changes between e.g. C and F, we should not get unrelated changes
// from B and D.
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
std::fs::write(repo_path.join("file1"), "a\n").unwrap();
std::fs::write(repo_path.join("file2"), "a\n").unwrap();
std::fs::write(repo_path.join("file3"), "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(repo_path.join("file3"), "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
std::fs::write(repo_path.join("file1"), "c\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["edit", "a"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "d"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "d"]);
std::fs::write(repo_path.join("file3"), "d\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "e"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "e"]);
std::fs::write(repo_path.join("file2"), "e\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "f"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "f"]);
std::fs::write(repo_path.join("file2"), "f\n").unwrap();
// Test the setup
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ -469,20 +469,20 @@ fn test_squash_from_to_partial() {
// D B
// |/
// A
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
std::fs::write(repo_path.join("file1"), "a\n").unwrap();
std::fs::write(repo_path.join("file2"), "a\n").unwrap();
std::fs::write(repo_path.join("file3"), "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(repo_path.join("file3"), "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
std::fs::write(repo_path.join("file1"), "c\n").unwrap();
std::fs::write(repo_path.join("file2"), "c\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["edit", "a"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "d"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "d"]);
std::fs::write(repo_path.join("file3"), "d\n").unwrap();
// Test the setup
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ -651,22 +651,22 @@ fn test_squash_from_multiple() {
// \|/
// A
let file = repo_path.join("file");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
std::fs::write(&file, "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(&file, "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "@-"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
std::fs::write(&file, "c\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "@-"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "d"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "d"]);
std::fs::write(&file, "d\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "all:visible_heads()"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "e"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "e"]);
std::fs::write(&file, "e\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "f"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "f"]);
std::fs::write(&file, "f\n").unwrap();
// Test the setup
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ -770,27 +770,27 @@ fn test_squash_from_multiple_partial() {
// A
let file1 = repo_path.join("file1");
let file2 = repo_path.join("file2");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
std::fs::write(&file1, "a\n").unwrap();
std::fs::write(&file2, "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(&file1, "b\n").unwrap();
std::fs::write(&file2, "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "@-"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
std::fs::write(&file1, "c\n").unwrap();
std::fs::write(&file2, "c\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "@-"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "d"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "d"]);
std::fs::write(&file1, "d\n").unwrap();
std::fs::write(&file2, "d\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "all:visible_heads()"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "e"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "e"]);
std::fs::write(&file1, "e\n").unwrap();
std::fs::write(&file2, "e\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "f"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "f"]);
std::fs::write(&file1, "f\n").unwrap();
std::fs::write(&file2, "f\n").unwrap();
// Test the setup
@ -1005,7 +1005,7 @@ fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
let template = r#"separate(
" ",
commit_id.short(),
branches,
bookmarks,
description,
if(empty, "(empty)")
)"#;

View file

@ -33,7 +33,7 @@ fn create_commit(
for (name, content) in files {
std::fs::write(repo_path.join(name), content).unwrap();
}
test_env.jj_cmd_ok(repo_path, &["branch", "create", name]);
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", name]);
}
#[test]
@ -77,7 +77,7 @@ fn test_status_merge() {
std::fs::write(repo_path.join("file"), "base").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "-m=left"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "left"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "left"]);
test_env.jj_cmd_ok(&repo_path, &["new", "@-", "-m=right"]);
std::fs::write(repo_path.join("file"), "right").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "left", "@"]);

View file

@ -32,20 +32,20 @@ fn test_tag_list() {
};
test_env.jj_cmd_ok(&repo_path, &["new", "root()", "-mcommit1"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "branch1"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "bookmark1"]);
test_env.jj_cmd_ok(&repo_path, &["new", "root()", "-mcommit2"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "branch2"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "bookmark2"]);
test_env.jj_cmd_ok(&repo_path, &["new", "root()", "-mcommit3"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "branch3"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "bookmark3"]);
test_env.jj_cmd_ok(&repo_path, &["git", "export"]);
copy_ref("refs/heads/branch1", "refs/tags/test_tag");
copy_ref("refs/heads/branch2", "refs/tags/test_tag2");
copy_ref("refs/heads/branch1", "refs/tags/conflicted_tag");
copy_ref("refs/heads/bookmark1", "refs/tags/test_tag");
copy_ref("refs/heads/bookmark2", "refs/tags/test_tag2");
copy_ref("refs/heads/bookmark1", "refs/tags/conflicted_tag");
test_env.jj_cmd_ok(&repo_path, &["git", "import"]);
copy_ref("refs/heads/branch2", "refs/tags/conflicted_tag");
copy_ref("refs/heads/bookmark2", "refs/tags/conflicted_tag");
test_env.jj_cmd_ok(&repo_path, &["git", "import"]);
copy_ref("refs/heads/branch3", "refs/tags/conflicted_tag");
copy_ref("refs/heads/bookmark3", "refs/tags/conflicted_tag");
test_env.jj_cmd_ok(&repo_path, &["git", "import", "--at-op=@-"]);
test_env.jj_cmd_ok(&repo_path, &["status"]); // resolve concurrent ops

View file

@ -56,7 +56,7 @@ fn test_git_push_undo() {
let repo_path = test_env.env_root().join("repo");
test_env.advance_test_rng_seed_to_multiple_of(100_000);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "AA"]);
test_env.jj_cmd_ok(&repo_path, &["git", "push"]);
test_env.advance_test_rng_seed_to_multiple_of(100_000);
@ -68,7 +68,7 @@ fn test_git_push_undo() {
// ------------------------------------------
// local `main` | BB | -- | --
// remote-tracking | AA | AA | AA
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 2080bdb8 (empty) AA
"###);
@ -80,7 +80,7 @@ fn test_git_push_undo() {
// ------------------------------------------
// local `main` | BB | -- | --
// remote-tracking | BB | BB | BB
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@origin: qpvuntsm 75e78001 (empty) BB
"###);
@ -93,7 +93,7 @@ fn test_git_push_undo() {
// ------------------------------------------
// local `main` | BB | -- | --
// remote-tracking | AA | AA | BB
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 2080bdb8 (empty) AA
"###);
@ -106,9 +106,9 @@ fn test_git_push_undo() {
// have been).
//
// One option to solve this would be to have undo not restore remote-tracking
// branches, but that also has undersired consequences: the second fetch in `jj
// git fetch && jj undo && jj git fetch` would become a no-op.
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
// bookmarks, but that also has undersired consequences: the second fetch in
// `jj git fetch && jj undo && jj git fetch` would become a no-op.
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main (conflicted):
- qpvuntsm hidden 2080bdb8 (empty) AA
+ qpvuntsm?? 20b2cc4b (empty) CC
@ -129,7 +129,7 @@ fn test_git_push_undo_with_import() {
let repo_path = test_env.env_root().join("repo");
test_env.advance_test_rng_seed_to_multiple_of(100_000);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "AA"]);
test_env.jj_cmd_ok(&repo_path, &["git", "push"]);
test_env.advance_test_rng_seed_to_multiple_of(100_000);
@ -141,7 +141,7 @@ fn test_git_push_undo_with_import() {
// ------------------------------------------
// local `main` | BB | -- | --
// remote-tracking | AA | AA | AA
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 2080bdb8 (empty) AA
"###);
@ -153,7 +153,7 @@ fn test_git_push_undo_with_import() {
// ------------------------------------------
// local `main` | BB | -- | --
// remote-tracking | BB | BB | BB
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@origin: qpvuntsm 75e78001 (empty) BB
"###);
@ -166,14 +166,14 @@ fn test_git_push_undo_with_import() {
// ------------------------------------------
// local `main` | BB | -- | --
// remote-tracking | AA | AA | BB
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 2080bdb8 (empty) AA
"###);
// PROBLEM: inserting this import changes the outcome compared to previous test
// TODO: decide if this is the better behavior, and whether import of
// remote-tracking branches should happen on every operation.
// remote-tracking bookmarks should happen on every operation.
test_env.jj_cmd_ok(&repo_path, &["git", "import"]);
// | jj refs | jj's | git
// | | git | repo
@ -181,7 +181,7 @@ fn test_git_push_undo_with_import() {
// ------------------------------------------
// local `main` | BB | -- | --
// remote-tracking | BB | BB | BB
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@origin: qpvuntsm 75e78001 (empty) BB
"###);
@ -190,7 +190,7 @@ fn test_git_push_undo_with_import() {
test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]);
// There is not a conflict. This seems like a good outcome; undoing `git push`
// was essentially a no-op.
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 20b2cc4b (empty) CC
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 75e78001 (empty) BB
"###);
@ -209,7 +209,7 @@ fn test_git_push_undo_colocated() {
test_env.jj_cmd_ok(&repo_path, &["git", "init", "--git-repo=."]);
test_env.advance_test_rng_seed_to_multiple_of(100_000);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "AA"]);
test_env.jj_cmd_ok(&repo_path, &["git", "push"]);
test_env.advance_test_rng_seed_to_multiple_of(100_000);
@ -221,7 +221,7 @@ fn test_git_push_undo_colocated() {
// ------------------------------------------
// local `main` | BB | BB | BB
// remote-tracking | AA | AA | AA
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@git: qpvuntsm 75e78001 (empty) BB
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 2080bdb8 (empty) AA
@ -234,7 +234,7 @@ fn test_git_push_undo_colocated() {
// ------------------------------------------
// local `main` | BB | BB | BB
// remote-tracking | BB | BB | BB
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@git: qpvuntsm 75e78001 (empty) BB
@origin: qpvuntsm 75e78001 (empty) BB
@ -256,7 +256,7 @@ fn test_git_push_undo_colocated() {
// ------------------------------------------
// local `main` | BB | BB | BB
// remote-tracking | AA | AA | AA
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@git: qpvuntsm 75e78001 (empty) BB
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 2080bdb8 (empty) AA
@ -266,7 +266,7 @@ fn test_git_push_undo_colocated() {
test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]);
// We have the same conflict as `test_git_push_undo`. TODO: why did we get the
// same result in a seemingly different way?
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main (conflicted):
- qpvuntsm hidden 2080bdb8 (empty) AA
+ qpvuntsm?? 20b2cc4b (empty) CC
@ -277,7 +277,7 @@ fn test_git_push_undo_colocated() {
}
// This test is currently *identical* to `test_git_push_undo` except
// both the git_refs and the remote-tracking branches are preserved by undo.
// both the git_refs and the remote-tracking bookmarks are preserved by undo.
// TODO: Investigate the different outcome
#[test]
fn test_git_push_undo_repo_only() {
@ -289,28 +289,28 @@ fn test_git_push_undo_repo_only() {
let repo_path = test_env.env_root().join("repo");
test_env.advance_test_rng_seed_to_multiple_of(100_000);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "AA"]);
test_env.jj_cmd_ok(&repo_path, &["git", "push"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 2080bdb8 (empty) AA
@origin: qpvuntsm 2080bdb8 (empty) AA
"###);
test_env.advance_test_rng_seed_to_multiple_of(100_000);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "BB"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 2080bdb8 (empty) AA
"###);
let pre_push_opid = test_env.current_operation_id(&repo_path);
test_env.jj_cmd_ok(&repo_path, &["git", "push"]);
// Undo the push, but keep both the git_refs and the remote-tracking branches
// Undo the push, but keep both the git_refs and the remote-tracking bookmarks
test_env.jj_cmd_ok(
&repo_path,
&["op", "restore", "--what=repo", &pre_push_opid],
);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 75e78001 (empty) BB
@origin: qpvuntsm 75e78001 (empty) BB
"###);
@ -318,14 +318,14 @@ fn test_git_push_undo_repo_only() {
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "CC"]);
test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]);
// This currently gives an identical result to `test_git_push_undo_import`.
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
main: qpvuntsm 20b2cc4b (empty) CC
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 75e78001 (empty) BB
"###);
}
#[test]
fn test_branch_track_untrack_undo() {
fn test_bookmark_track_untrack_undo() {
let test_env = TestEnvironment::default();
test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#);
let git_repo_path = test_env.env_root().join("git-repo");
@ -334,10 +334,10 @@ fn test_branch_track_untrack_undo() {
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["describe", "-mcommit"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "feature1", "feature2"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "feature1", "feature2"]);
test_env.jj_cmd_ok(&repo_path, &["git", "push"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "delete", "feature2"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
test_env.jj_cmd_ok(&repo_path, &["bookmark", "delete", "feature2"]);
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
feature1: qpvuntsm 8da1cfc8 (empty) commit
@origin: qpvuntsm 8da1cfc8 (empty) commit
feature2 (deleted)
@ -347,16 +347,16 @@ fn test_branch_track_untrack_undo() {
// Track/untrack can be undone so long as states can be trivially merged.
test_env.jj_cmd_ok(
&repo_path,
&["branch", "untrack", "feature1@origin", "feature2@origin"],
&["bookmark", "untrack", "feature1@origin", "feature2@origin"],
);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
feature1: qpvuntsm 8da1cfc8 (empty) commit
feature1@origin: qpvuntsm 8da1cfc8 (empty) commit
feature2@origin: qpvuntsm 8da1cfc8 (empty) commit
"###);
test_env.jj_cmd_ok(&repo_path, &["undo"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
feature1: qpvuntsm 8da1cfc8 (empty) commit
@origin: qpvuntsm 8da1cfc8 (empty) commit
feature2 (deleted)
@ -364,28 +364,28 @@ fn test_branch_track_untrack_undo() {
"###);
test_env.jj_cmd_ok(&repo_path, &["undo"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
feature1: qpvuntsm 8da1cfc8 (empty) commit
feature1@origin: qpvuntsm 8da1cfc8 (empty) commit
feature2@origin: qpvuntsm 8da1cfc8 (empty) commit
"###);
test_env.jj_cmd_ok(&repo_path, &["branch", "track", "feature1@origin"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
test_env.jj_cmd_ok(&repo_path, &["bookmark", "track", "feature1@origin"]);
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
feature1: qpvuntsm 8da1cfc8 (empty) commit
@origin: qpvuntsm 8da1cfc8 (empty) commit
feature2@origin: qpvuntsm 8da1cfc8 (empty) commit
"###);
test_env.jj_cmd_ok(&repo_path, &["undo"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
feature1: qpvuntsm 8da1cfc8 (empty) commit
feature1@origin: qpvuntsm 8da1cfc8 (empty) commit
feature2@origin: qpvuntsm 8da1cfc8 (empty) commit
"###);
}
fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
// --quiet to suppress deleted branches hint
test_env.jj_cmd_success(repo_path, &["branch", "list", "--all-remotes", "--quiet"])
fn get_bookmark_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
// --quiet to suppress deleted bookmarks hint
test_env.jj_cmd_success(repo_path, &["bookmark", "list", "--all-remotes", "--quiet"])
}

View file

@ -23,13 +23,13 @@ fn test_unsquash() {
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
std::fs::write(repo_path.join("file1"), "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(repo_path.join("file1"), "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
std::fs::write(repo_path.join("file1"), "c\n").unwrap();
// Test the setup
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ -84,10 +84,10 @@ fn test_unsquash() {
test_env.jj_cmd_ok(&repo_path, &["undo"]);
test_env.jj_cmd_ok(&repo_path, &["edit", "b"]);
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "d"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "d"]);
std::fs::write(repo_path.join("file2"), "d\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new", "-m", "merge", "c", "d"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "e"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "e"]);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
@ b780e7469252 e
@ -135,15 +135,15 @@ fn test_unsquash_partial() {
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "a"]);
std::fs::write(repo_path.join("file1"), "a\n").unwrap();
std::fs::write(repo_path.join("file2"), "a\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "b"]);
std::fs::write(repo_path.join("file1"), "b\n").unwrap();
std::fs::write(repo_path.join("file2"), "b\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["new"]);
test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]);
test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "c"]);
std::fs::write(repo_path.join("file1"), "c\n").unwrap();
std::fs::write(repo_path.join("file2"), "c\n").unwrap();
// Test the setup
@ -256,7 +256,7 @@ fn test_unsquash_partial() {
}
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
let template = r#"commit_id.short() ++ " " ++ branches"#;
let template = r#"commit_id.short() ++ " " ++ bookmarks"#;
test_env.jj_cmd_success(repo_path, &["log", "-T", template])
}

View file

@ -1,19 +1,19 @@
# Frequently asked questions
### Why does my branch not move to the new commit after `jj new/commit`?
### Why does my bookmark not move to the new commit after `jj new/commit`?
If you're familiar with Git, you might expect the current branch to move forward
when you commit. However, Jujutsu does not have a concept of a "current branch".
If you're familiar with Git, you might expect the current bookmark to move forward
when you commit. However, Jujutsu does not have a concept of a "current bookmark".
To move branches, use `jj branch set`.
To move bookmarks, use `jj bookmark set`.
### I made a commit and `jj git push --all` says "Nothing changed" instead of pushing it. What do I do?
`jj git push --all` pushes all _branches_, not all revisions. You have two
`jj git push --all` pushes all _bookmarks_, not all revisions. You have two
options:
* Using `jj git push --change` will automatically create a branch and push it.
* Using `jj branch` commands to create or move a branch to either the commit
* Using `jj git push --change` will automatically create a bookmark and push it.
* Using `jj bookmark` commands to create or move a bookmark to either the commit
you want to push or a descendant on it. Unlike Git, Jujutsu doesn't do this
automatically (see previous question).
@ -262,15 +262,15 @@ this is to abandon the unneeded commits (using `jj abandon <commit ID>`). If you
would like to keep both commits with this change ID, you can `jj duplicate` one
of them before abandoning it.
### How do I deal with conflicted branches ('??' after branch name)?
### How do I deal with conflicted bookmarks ('??' after bookmark name)?
A [conflicted branch][branches_conflicts] is a branch that refers to multiple
A [conflicted bookmark][bookmarks_conflicts] is a bookmark that refers to multiple
different commits because jj couldn't fully resolve its desired position.
Resolving conflicted branches is usually done by setting the branch to the
correct commit using `jj branch set <commit ID>`.
Resolving conflicted bookmarks is usually done by setting the bookmark to the
correct commit using `jj bookmark set <commit ID>`.
Usually, the different commits associated with the conflicted branch should all
appear in the log, but if they don't you can use `jj branch list`to show all the
Usually, the different commits associated with the conflicted bookmark should all
appear in the log, but if they don't you can use `jj bookmark list`to show all the
commits associated with it.
### How do I integrate Jujutsu with Gerrit?
@ -282,15 +282,15 @@ contributor (look for the `jj signoff` alias).
After you have attached the `Change-Id:` footer to the commit series, you'll
have to manually invoke `git push` of `HEAD` on the underlying git repository
into the remote Gerrit branch `refs/for/$BRANCH`, where `$BRANCH` is the base
branch you want your changes to go to (e.g., `git push origin
into the remote Gerrit bookmark `refs/for/$BRANCH`, where `$BRANCH` is the base
bookmark you want your changes to go to (e.g., `git push origin
HEAD:refs/for/main`). Using a [co-located][co-located] repo
will make the underlying git repo directly accessible from the working
directory.
We hope to integrate with Gerrit natively in the future.
[branches_conflicts]: branches.md#conflicts
[bookmarks_conflicts]: bookmarks.md#conflicts
[change ID]: glossary.md#change-id
[co-located]: glossary.md#co-located-repos

221
docs/bookmarks.md Normal file
View file

@ -0,0 +1,221 @@
# Bookmarks
## Introduction
Bookmarks are named pointers to revisions (just like branches are in Git). You
can move them without affecting the target revision's identity. Bookmarks
automatically move when revisions are rewritten (e.g. by `jj rebase`). You can
pass a bookmark's name to commands that want a revision as argument. For example,
`jj new main` will create a new revision on top of the "main" bookmark. Use
`jj bookmark list` to list bookmarks and `jj bookmark` to create, move, or delete
bookmarks. There is currently no concept of an active/current/checked-out bookmark.
Currently Jujutsu maps its Bookmarks to Git Branches and stores them as that
in the Git backend. This means that all Bookmarks will be reflected as
Git Branches, this may change in the future.
## Remotes and tracked bookmarks
Jujutsu records the last seen position of a bookmark on each remote (just like
Git's remote-tracking branches). This record is updated on every `jj git fetch`
and `jj git push` of the bookmark. You can refer to the remembered remote bookmark
positions with `<bookmark name>@<remote name>`, such as `jj new main@origin`. `jj`
does not provide a way to manually edit these recorded positions.
A remote bookmark can be associated with a local bookmark of the same name. This is
called a **tracked remote bookmark**, which currently maps to a Git remote
branch. When you pull a tracked bookmark from a remote, any changes compared to
the current record of the remote's state will be propagated to the corresponding
local bookmark, which will be created if it doesn't exist already.
!!! note "Details: how `fetch` pulls bookmarks"
Let's say you run `jj git fetch --remote origin` and, during the fetch, `jj`
determines that the remote's "main" bookmark has been moved so that its target is
now ahead of the local record in `main@origin`.
`jj` will then update `main@origin` to the new target. If `main@origin` is
**tracked**, `jj` will also apply the change to the local bookmark `main`. If the
local target has also been moved compared to `main@origin` (probably because you
ran `jj bookmark set main`), then the two updates will be merged. If one is ahead
of the other, then that target will become the new target. Otherwise, the local
bookmark will become conflicted (see the ["Conflicts" section](#conflicts) below
for details).
Most commands don't show the tracked remote bookmark if it has the same target as
the local bookmark. The local bookmark (without `@<remote name>`) is considered the
bookmark's desired target. Consequently, if you want to update a bookmark on a
remote, you first update the bookmark locally and then push the update to the
remote. If a local bookmark also exists on some remote but points to a different
target there, `jj log` will show the bookmark name with an asterisk suffix (e.g.
`main*`). That is meant to remind you that you may want to push the bookmark to
some remote.
If you want to know the internals of bookmark tracking, consult the
[Design Doc][design].
### Terminology summary
- A **remote bookmark** is a bookmark ref on the remote. `jj` can find out its
actual state only when it's actively communicating with the remote. However,
`jj` does store the last-seen position of the remote bookmark; this is the
commit `jj show <bookmark name>@<remote name>` would show. This notion is
completely analogous to Git's "remote-tracking bookmarks".
- A **tracked (remote) bookmark** is defined above. You can make a remote bookmark
tracked with the [`jj bookmark track` command](#manually-tracking-a-bookmark), for
example.
- A **tracking (local) bookmark** is the local bookmark that `jj` tries to keep in
sync with the tracked remote bookmark. For example, after `jj bookmark track
mybookmark@origin`, there will be a local bookmark `mybookmark` that's tracking the
remote `mybookmark@origin` bookmark. A local bookmark can track a bookmark of the same
name on 0 or more remotes.
The notion of tracked bookmarks serves a similar function to the Git notion of an
"upstream branch". Unlike Git, a single local bookmark can be tracking remote
bookmarks on multiple remotes, and the names of the local and remote bookmarks
must match.
### Manually tracking a bookmark
To track a bookmark permanently use `jj bookmark track <bookmark name>@<remote name>`.
It will now be imported as a local bookmark until you untrack it or it is deleted
on the remote.
Example:
```sh
$ # List all available bookmarks, as we want our colleague's bookmark.
$ jj bookmark list --all
$ # Find the bookmark.
$ # [...]
$ # Actually track the bookmark.
$ jj bookmark track <bookmark name>@<remote name> # Example: jj bookmark track my-feature@origin
$ # From this point on, <bookmark name> will be imported when fetching from <remote name>.
$ jj git fetch --remote <remote name>
$ # A local bookmark <bookmark name> should have been created or updated while fetching.
$ jj new <bookmark name> # Do some local testing, etc.
```
### Untracking a bookmark
To stop following a remote bookmark, you can `jj bookmark untrack` it. After that,
subsequent fetches of that remote will no longer move the local bookmark to match
the position of the remote bookmark.
Example:
```sh
$ # List all local and remote bookmarks.
$ jj bookmark list --all
$ # Find the bookmark we no longer want to track.
$ # [...]
# # Actually untrack it.
$ jj bookmark untrack <bookmark name>@<remote name> # Example: jj bookmark untrack stuff@origin
$ # From this point on, this remote bookmark won't be imported anymore.
$ # The local bookmark (e.g. stuff) is unaffected. It may or may not still
$ # be tracking bookmarks on other remotes (e.g. stuff@upstream).
```
### Listing tracked bookmarks
To list tracked bookmarks, you can `jj bookmark list --tracked` or `jj bookmark list -t`.
This command omits local Git-tracking bookmarks by default.
You can see if a specific bookmark is tracked with `jj bookmark list --tracked <bookmark name>`.
### Automatic tracking of bookmarks & `git.auto-local-bookmark` option
There are two situations where `jj` tracks bookmarks automatically. `jj git
clone` automatically sets up the default remote bookmark (e.g. `main@origin`) as
tracked. When you push a local bookmark, the newly created bookmark on the remote is
marked as tracked.
By default, every other remote bookmark is marked as "not tracked" when it's
fetched. If desired, you need to manually `jj bookmark track` them. This works
well for repositories where multiple people work on a large number of bookmarks.
The default can be changed by setting the config `git.auto-local-bookmark = true`.
Then, `jj git fetch` tracks every *newly fetched* bookmark with a local bookmark.
Branches that already existed before the `jj git fetch` are not affected. This
is similar to Mercurial, which fetches all its bookmarks (equivalent to Git
bookmarks) by default.
## Bookmark movement
Currently Jujutsu automatically moves local bookmarks when these conditions are
met:
* When a commit has been rewritten (e.g, when you rebase) bookmarks and the
working-copy will move along with it.
* When a commit has been abandoned, all associated bookmarks will be moved
to its parent(s). If a working copy was pointing to the abandoned commit,
then a new working-copy commit will be created on top of the parent(s).
You could describe the movement as following along the change-id of the
current bookmark commit, even if it isn't entirely accurate.
## Pushing bookmarks: Safety checks
Before `jj git push` actually moves, creates, or deletes a remote bookmark, it
makes several safety checks.
1. `jj` will contact the remote and check that the actual state of the remote
bookmark matches `jj`'s record of its last known position. If there is a
conflict, `jj` will refuse to push the bookmark. In this case, you need to run
`jj git fetch --remote <remote name>` and resolve the resulting bookmark
conflict. Then, you can try `jj git push` again.
If you are familiar with Git, this makes `jj git push` similar to `git
push --force-with-lease`.
There are a few cases where `jj git push` will succeed even though the remote
bookmark is in an unexpected location. These are the cases where `jj git fetch`
would not create a bookmark conflict and would not move the local bookmark, e.g.
if the unexpected location is identical to the local position of the bookmark.
2. The local bookmark must not be [conflicted](#conflicts). If it is, you would
need to use `jj bookmark set`, for example, to resolve the conflict.
This makes `jj git push` safe even if `jj git fetch` is performed on a timer
in the background (this situation is a known issue[^known-issue] with some
forms of `git push --force-with-lease`). If the bookmark moves on a remote in a
problematic way, `jj git fetch` will create a conflict. This should ensure
that the user becomes aware of the conflict before they can `jj git push` and
override the bookmark on the remote.
3. If the remote bookmark already exists on the remote, it must be
[tracked](#remotes-and-tracked-bookmarks). If the bookmark does not already
exist on the remote, there is no problem; `jj git push` will create the
remote bookmark and mark it as tracked.
[^known-issue]: See "A general note on safety" in
<https://git-scm.com/docs/git-push#Documentation/git-push.txt---no-force-with-lease>
## Conflicts
Bookmarks can end up in a conflicted state. When that happens, `jj status` will
include information about the conflicted bookmarks (and instructions for how to
mitigate it). `jj bookmark list` will have details. `jj log` will show the bookmark
name with a double question mark suffix (e.g. `main??`) on each of the
conflicted bookmark's potential target revisions. Using the bookmark name to look up
a revision will resolve to all potential targets. That means that `jj new main`
will error out, complaining that the revset resolved to multiple revisions.
Both local bookmarks (e.g. `main`) and the remote bookmark (e.g. `main@origin`) can
have conflicts. Both can end up in that state if concurrent operations were run
in the repo. The local bookmark more typically becomes conflicted because it was
updated both locally and on a remote.
To resolve a conflicted state in a local bookmark (e.g. `main`), you can move the
bookmark to the desired target with `jj bookmark move`. You may want to first either
merge the conflicted targets with `jj new` (e.g. `jj new 'all:main'`), or you may
want to rebase one side on top of the other with `jj rebase`.
To resolve a conflicted state in a remote bookmark (e.g. `main@origin`), simply
pull from the remote (e.g. `jj git fetch`). The conflict resolution will also
propagate to the local bookmark (which was presumably also conflicted).
[design]: design/tracking-branches.md

View file

@ -1,217 +0,0 @@
# Branches
## Introduction
Branches are named pointers to revisions (just like they are in Git). You can
move them without affecting the target revision's identity. Branches
automatically move when revisions are rewritten (e.g. by `jj rebase`). You can
pass a branch's name to commands that want a revision as argument. For example,
`jj new main` will create a new revision on top of the "main" branch. Use
`jj branch list` to list branches and `jj branch` to create, move, or delete
branches. There is currently no concept of an active/current/checked-out branch.
## Remotes and tracked branches
Jujutsu records the last seen position of a branch on each remote (just like
Git's remote-tracking branches). This record is updated on every `jj git fetch`
and `jj git push` of the branch. You can refer to the remembered remote branch
positions with `<branch name>@<remote name>`, such as `jj new main@origin`. `jj`
does not provide a way to manually edit these recorded positions.
A remote branch can be associated with a local branch of the same name. This is
called a **tracked remote branch**. When you pull a tracked branch from a
remote, any changes compared to the current record of the remote's state will be
propagated to the corresponding local branch, which will be created if it
doesn't exist already.
!!! note "Details: how `fetch` pulls branches"
Let's say you run `jj git fetch --remote origin` and, during the fetch, `jj`
determines that the remote's "main" branch has been moved so that its target is
now ahead of the local record in `main@origin`.
`jj` will then update `main@origin` to the new target. If `main@origin` is
**tracked**, `jj` will also apply the change to the local branch `main`. If the
local target has also been moved compared to `main@origin` (probably because you
ran `jj branch set main`), then the two updates will be merged. If one is ahead
of the other, then that target will become the new target. Otherwise, the local
branch will become conflicted (see the ["Conflicts" section](#conflicts) below
for details).
Most commands don't show the tracked 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. 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.
If you want to know the internals of branch tracking, consult the
[Design Doc][design].
### Terminology summary
- A **remote branch** is a branch ref on the remote. `jj` can find out its
actual state only when it's actively communicating with the remote. However,
`jj` does store the last-seen position of the remote branch; this is the
commit `jj show <branch name>@<remote name>` would show. This notion is
completely analogous to Git's "remote-tracking branches".
- A **tracked (remote) branch** is defined above. You can make a remote branch
tracked with the [`jj branch track` command](#manually-tracking-a-branch), for
example.
- A **tracking (local) branch** is the local branch that `jj` tries to keep in
sync with the tracked remote branch. For example, after `jj branch track
mybranch@origin`, there will be a local branch `mybranch` that's tracking the
remote `mybranch@origin` branch. A local branch can track a branch of the same
name on 0 or more remotes.
The notion of tracked branches serves a similar function to the Git notion of an
"upstream branch". Unlike Git, a single local branch can be tracking remote
branches on multiple remotes, and the names of the local and remote branches
must match.
### Manually tracking a branch
To track a branch permanently use `jj branch track <branch name>@<remote name>`.
It will now be imported as a local branch until you untrack it or it is deleted
on the remote.
Example:
```sh
$ # List all available branches, as we want our colleague's branch.
$ jj branch list --all
$ # Find the branch.
$ # [...]
$ # Actually track the branch.
$ jj branch track <branch name>@<remote name> # Example: jj branch track my-feature@origin
$ # From this point on, <branch name> will be imported when fetching from <remote name>.
$ jj git fetch --remote <remote name>
$ # A local branch <branch name> should have been created or updated while fetching.
$ jj new <branch name> # Do some local testing, etc.
```
### Untracking a branch
To stop following a remote branch, you can `jj branch untrack` it. After that,
subsequent fetches of that remote will no longer move the local branch to match
the position of the remote branch.
Example:
```sh
$ # List all local and remote branches.
$ jj branch list --all
$ # Find the branch we no longer want to track.
$ # [...]
# # Actually untrack it.
$ jj branch untrack <branch name>@<remote name> # Example: jj branch untrack stuff@origin
$ # From this point on, this remote branch won't be imported anymore.
$ # The local branch (e.g. stuff) is unaffected. It may or may not still
$ # be tracking branches on other remotes (e.g. stuff@upstream).
```
### Listing tracked branches
To list tracked branches, you can `jj branch list --tracked` or `jj branch list -t`.
This command omits local Git-tracking branches by default.
You can see if a specific branch is tracked with `jj branch list --tracked <branch name>`.
### Automatic tracking of branches & `git.auto-local-branch` option
There are two situations where `jj` tracks branches automatically. `jj git
clone` automatically sets up the default remote branch (e.g. `main@origin`) as
tracked. When you push a local branch, the newly created branch on the remote is
marked as tracked.
By default, every other remote branch is marked as "not tracked" when it's
fetched. If desired, you need to manually `jj branch track` them. This works
well for repositories where multiple people work on a large number of branches.
The default can be changed by setting the config `git.auto-local-branch = true`.
Then, `jj git fetch` tracks every *newly fetched* branch with a local branch.
Branches that already existed before the `jj git fetch` are not affected. This
is similar to Mercurial, which fetches all its bookmarks (equivalent to Git
branches) by default.
## Branch movement
Currently Jujutsu automatically moves local branches when these conditions are
met:
* When a commit has been rewritten (e.g, when you rebase) branches and the
working-copy will move along with it.
* When a commit has been abandoned, all associated branches will be moved
to its parent(s). If a working copy was pointing to the abandoned commit,
then a new working-copy commit will be created on top of the parent(s).
You could describe the movement as following along the change-id of the
current branch commit, even if it isn't entirely accurate.
## Pushing branches: Safety checks
Before `jj git push` actually moves, creates, or deletes a remote branch, it
makes several safety checks.
1. `jj` will contact the remote and check that the actual state of the remote
branch matches `jj`'s record of its last known position. If there is a
conflict, `jj` will refuse to push the branch. In this case, you need to run
`jj git fetch --remote <remote name>` and resolve the resulting branch
conflict. Then, you can try `jj git push` again.
If you are familiar with Git, this makes `jj git push` similar to `git
push --force-with-lease`.
There are a few cases where `jj git push` will succeed even though the remote
branch is in an unexpected location. These are the cases where `jj git fetch`
would not create a branch conflict and would not move the local branch, e.g.
if the unexpected location is identical to the local position of the branch.
2. The local branch must not be [conflicted](#conflicts). If it is, you would
need to use `jj branch set`, for example, to resolve the conflict.
This makes `jj git push` safe even if `jj git fetch` is performed on a timer
in the background (this situation is a known issue[^known-issue] with some
forms of `git push --force-with-lease`). If the branch moves on a remote in a
problematic way, `jj git fetch` will create a conflict. This should ensure
that the user becomes aware of the conflict before they can `jj git push` and
override the branch on the remote.
3. If the remote branch already exists on the remote, it must be
[tracked](#remotes-and-tracked-branches). If the branch does not already
exist on the remote, there is no problem; `jj git push` will create the
remote branch and mark it as tracked.
[^known-issue]: See "A general note on safety" in
<https://git-scm.com/docs/git-push#Documentation/git-push.txt---no-force-with-lease>
## Conflicts
Branches can end up in a conflicted state. When that happens, `jj status` will
include information about the conflicted branches (and instructions for how to
mitigate it). `jj branch list` will have details. `jj log` will show the branch
name with a double question mark suffix (e.g. `main??`) on each of the
conflicted branch's potential target revisions. Using the branch name to look up
a revision will resolve to all potential targets. That means that `jj new main`
will error out, complaining that the revset resolved to multiple revisions.
Both local branches (e.g. `main`) and the remote branch (e.g. `main@origin`) can
have conflicts. Both can end up in that state if divergent operations were run
in the repo. The local branch more typically becomes conflicted because it was
updated both locally and on a remote.
To resolve a conflicted state in a local branch (e.g. `main`), you can move the
branch to the desired target with `jj branch move`. You may want to first either
merge the conflicted targets with `jj new` (e.g. `jj new 'all:main'`), or you may
want to rebase one side on top of the other with `jj rebase`.
To resolve a conflicted state in a remote branch (e.g. `main@origin`), simply
pull from the remote (e.g. `jj git fetch`). The conflict resolution will also
propagate to the local branch (which was presumably also conflicted).
[design]: design/tracking-branches.md

View file

@ -266,7 +266,7 @@ diff-invocation-mode = "file-by-file"
You can configure the set of immutable commits via
`revset-aliases."immutable_heads()"`. The default set of immutable heads is
`trunk() | tags() | untracked_remote_branches()`. For example, to prevent
`trunk() | tags() | untracked_remote_bookmarks()`. For example, to prevent
rewriting commits on `main@origin` and commits authored by other users:
```toml
@ -357,7 +357,7 @@ To customize these separately, use the `format_short_commit_id()` and
To get shorter prefixes for certain revisions, set `revsets.short-prefixes`:
```toml
# Prioritize the current branch
# Prioritize the current bookmark
revsets.short-prefixes = "(main..@)::"
```
@ -475,8 +475,8 @@ format = "git"
You can define aliases for commands, including their arguments. For example:
```toml
# `jj l` shows commits on the working-copy commit's (anonymous) branch
# compared to the `main` branch
# `jj l` shows commits on the working-copy commit's (anonymous) bookmark
# compared to the `main` bookmark
aliases.l = ["log", "-r", "(main..@):: | (main..@)-"]
```
@ -861,32 +861,32 @@ Note that unlike `git.fetch`, `git.push` can currently only be a single remote.
This is not a hard limitation, and could be changed in the future if there is
demand.
### Automatic local branch creation
### Automatic local bookmark creation
When `jj` imports a new remote-tracking branch from Git, it can also create a
local branch with the same name. This feature is disabled by default because it
When `jj` imports a new remote-tracking bookmark from Git, it can also create a
local bookmark with the same name. This feature is disabled by default because it
may be undesirable in some repositories, e.g.:
- There is a remote with a lot of historical branches that you don't
- There is a remote with a lot of historical bookmarks that you don't
want to be exported to the co-located Git repo.
- There are multiple remotes with conflicting views of that branch,
- There are multiple remotes with conflicting views of that bookmark,
resulting in an unhelpful conflicted state.
You can enable this behavior by setting `git.auto-local-branch` like so,
You can enable this behavior by setting `git.auto-local-bookmark` like so,
```toml
git.auto-local-branch = true
git.auto-local-bookmark = true
```
This setting is applied only to new remote branches. Existing remote branches
can be tracked individually by using `jj branch track`/`untrack` commands.
This setting is applied only to new remote bookmarks. Existing remote bookmarks
can be tracked individually by using `jj bookmark track`/`untrack` commands.
```shell
# import feature1 branch and start tracking it
jj branch track feature1@origin
# delete local gh-pages branch and stop tracking it
jj branch delete gh-pages
jj branch untrack gh-pages@upstream
# import feature1 bookmark and start tracking it
jj bookmark track feature1@origin
# delete local gh-pages bookmark and stop tracking it
jj bookmark delete gh-pages
jj bookmark untrack gh-pages@upstream
```
### Abandon commits that became unreachable in Git
@ -903,13 +903,13 @@ git.abandon-unreachable-commits = false
[reachable]: https://git-scm.com/docs/gitglossary/#Documentation/gitglossary.txt-aiddefreachableareachable
### Prefix for generated branches on push
### Prefix for generated bookmarks on push
`jj git push --change` generates branch names with a prefix of "push-" by
default. You can pick a different prefix by setting `git.push-branch-prefix`. For
`jj git push --change` generates bookmark names with a prefix of "push-" by
default. You can pick a different prefix by setting `git.push-bookmark-prefix`. For
example:
git.push-branch-prefix = "martinvonz/push-"
git.push-bookmark-prefix = "martinvonz/push-"
### Set of private commits

View file

@ -45,7 +45,7 @@ is typical on GitHub). Instead, please make the changes in the appropriate
commit. You can do that by creating a new commit on top of the initial commit
(`jj new <commit>`) and then squash in the changes when you're done (`jj squash`).
`jj git push`
will automatically force-push the branch.
will automatically force-push the bookmark.
When your first PR has been approved, we typically give you contributor access,
so you can address any remaining minor comments and then merge the PR yourself
@ -284,11 +284,11 @@ Windows, you'll need to understand and adapt the shell script):
cloned from your fork of `jj` (e.g. `jjfan.github.com/jj`). You can also use a
pure Git repo if you prefer.
2. Make sure `jjfan.github.com/jj` includes the `gh-pages` branch of the jj repo
2. Make sure `jjfan.github.com/jj` includes the `gh-pages` bookmark of the jj repo
and run `git fetch origin gh-pages`.
3. Go to the GitHub repository settings, enable GitHub Pages, and configure them
to use the `gh-pages` branch (this is usually the default).
to use the `gh-pages` bookmark (this is usually the default).
4. Run the same `sh` script that is used in GitHub CI (details below):
@ -298,8 +298,8 @@ to use the `gh-pages` branch (this is usually the default).
```
This should build the version of the docs from the current commit,
deploy it as a new commit to the `gh-pages` branch,
and push the `gh-pages` branch to the origin.
deploy it as a new commit to the `gh-pages` bookmark,
and push the `gh-pages` bookmark to the origin.
5. Now, you should be able to see the full website, including your latest changes
to the `prerelease` version, at `https://jjfan.github.io/jj/prerelease/`.
@ -315,18 +315,18 @@ back and forth, you can also rebuild the docs for the latest release as follows.
v1.33.1 latest --push
```
7. (Optional) When you are done, you may want to reset the `gh-branches` to the
7. (Optional) When you are done, you may want to reset the `gh-bookmarks` to the
same spot as it is in the upstream. If you configured the `upstream` remote,
this can be done with:
```shell
# This will LOSE any changes you made to `gh-pages`
jj git fetch --remote upstream
jj branch set gh-pages -r gh-pages@upstream
jj git push --remote origin --branch gh-pages
jj bookmark set gh-pages -r gh-pages@upstream
jj git push --remote origin --bookmark gh-pages
```
If you want to preserve some of the changes you made, you can do `jj branch
If you want to preserve some of the changes you made, you can do `jj bookmark
set my-changes -r gh-pages` BEFORE running the above commands.
#### Explanation of the `docs-build-deploy` script
@ -341,10 +341,10 @@ deploy`, which does the rest of the job. Run `poetry run -- mike help deploy` to
find out what the arguments do.
If you need to do something more complicated, you can use `poetry run -- mike
...` commands. You can also edit the `gh-pages` branch directly, but take care
...` commands. You can also edit the `gh-pages` bookmark directly, but take care
to avoid files that will be overwritten by future invocations of `mike`. Then,
you can submit a PR based on the `gh-pages` branch of
<https://martinvonz.github.com/jj> (instead of the usual `main` branch).
you can submit a PR based on the `gh-pages` bookmark of
<https://martinvonz.github.com/jj> (instead of the usual `main` bookmark).
## Modifying protobuffers (this is not common)

View file

@ -22,19 +22,19 @@ various use cases.
to an intermediate commit between `HEAD` and the working copy, so workflows
that depend on it can be modeled using proper commits instead. Jujutsu has
excellent support for moving changes between commits. [Details](#the-index).
* **No need for branch names (but they are supported).** Git lets you check out
a commit without attaching a branch. It calls this state "detached HEAD". This
* **No need for bookmark names (but they are supported).** Git lets you check out
a commit without attaching a bookmark. It calls this state "detached HEAD". This
is the normal state in Jujutsu (there's actually no way -- yet, at least -- to
have an active branch). However, Jujutsu keeps track of all visible heads
have an active bookmark). However, Jujutsu keeps track of all visible heads
(leaves) of the commit graph, so the commits won't get lost or
garbage-collected.
* **No current branch.** Git lets you check out a branch, making it the 'current
branch', and new commits will automatically update the branch. This is
* **No current bookmark.** Git lets you check out a bookmark, making it the 'current
bookmark', and new commits will automatically update the bookmark. This is
necessary in Git because Git might otherwise lose track of the new commits.
Jujutsu does not have a 'current branch'; instead, you update branches
manually. For example, if you start work on top of a commit with a branch,
new commits are created on top of the branch, then you issue a later command
to update the branch.
Jujutsu does not have a 'current bookmark'; instead, you update bookmarks
manually. For example, if you start work on top of a commit with a bookmark,
new commits are created on top of the bookmark, then you issue a later command
to update the bookmark.
* **Conflicts can be committed.** No commands fail because of merge conflicts.
The conflicts are instead recorded in commits and you can resolve them later.
[Details](conflicts.md).
@ -44,10 +44,10 @@ various use cases.
updated, and so will the working copy if it points to any of the rebased
commits.
* **Branches are identified by their names (across remotes).** For example, if
you pull from a remote that has a `main` branch, you'll get a branch by that
you pull from a remote that has a `main` bookmark, you'll get a bookmark by that
name in your local repo as well. If you then move it and push back to the
remote, the `main` branch on the remote will be updated.
[Details](branches.md).
remote, the `main` bookmark on the remote will be updated.
[Details](bookmarks.md).
* **The operation log replaces reflogs.** The operation log is similar to
reflogs, but is much more powerful. It keeps track of atomic updates to all
refs at once (Jujutsu thus improves on Git's per-ref history much in the same
@ -56,7 +56,7 @@ various use cases.
* **There's a single, virtual root commit.** Like Mercurial, Jujutsu has a
virtual commit (with a hash consisting of only zeros) called the "root commit"
(called the "null revision" in Mercurial). This commit is a common ancestor of
all commits. That removes the awkward state Git calls the "unborn branch"
all commits. That removes the awkward state Git calls the "unborn bookmark"
state (which is the state a newly initialized Git repo is in), and related
command-line flags (e.g. `git rebase --root`, `git checkout --orphan`).
@ -110,23 +110,23 @@ parent.
<td><code>git clone &lt;source&gt; &lt;destination&gt;</code></td>
</tr>
<tr>
<td>Update the local repo with all branches from a remote</td>
<td>Update the local repo with all bookmarks from a remote</td>
<td><code>jj git fetch [--remote &lt;remote&gt;]</code> (there is no
support for fetching into non-Git repos yet)</td>
<td><code>git fetch [&lt;remote&gt;]</code></td>
</tr>
<tr>
<td>Update a remote repo with all branches from the local repo</td>
<td>Update a remote repo with all bookmarks from the local repo</td>
<td><code>jj git push --all [--remote &lt;remote&gt;]</code> (there is no
support for pushing from non-Git repos yet)</td>
<td><code>git push --all [&lt;remote&gt;]</code></td>
</tr>
<tr>
<td>Update a remote repo with a single branch from the local repo</td>
<td><code>jj git push --branch &lt;branch name&gt;
<td>Update a remote repo with a single bookmark from the local repo</td>
<td><code>jj git push --bookmark &lt;bookmark name&gt;
[--remote &lt;remote&gt;]</code> (there is no support for
pushing from non-Git repos yet)</td>
<td><code>git push &lt;remote&gt; &lt;branch name&gt;</code></td>
<td><code>git push &lt;remote&gt; &lt;bookmark name&gt;</code></td>
</tr>
<tr>
<td>Show summary of current work and repo status</td>
@ -187,10 +187,10 @@ parent.
<tr>
<td>See log of all reachable commits</td>
<td><code>jj log -r 'all()'</code> or <code>jj log -r ::</code></td>
<td><code>git log --oneline --graph --decorate --branches</code></td>
<td><code>git log --oneline --graph --decorate --bookmarks</code></td>
</tr>
<tr>
<td>Show log of commits not on the main branch</td>
<td>Show log of commits not on the main bookmark</td>
<td><code>jj log</code></td>
<td>(TODO)</td>
</tr>
@ -243,23 +243,23 @@ parent.
<td><code>git stash</code></td>
</tr>
<tr>
<td>Start working on a new change based on the &lt;main&gt; branch</td>
<td>Start working on a new change based on the &lt;main&gt; bookmark</td>
<td><code>jj new main</code></td>
<td><code>git switch -c topic main</code> or
<code>git checkout -b topic main</code> (may need to stash or commit
first)</td>
</tr>
<tr>
<td>Move branch A onto branch B</td>
<td>Move bookmark A onto bookmark B</td>
<td><code>jj rebase -b A -d B</code></td>
<td><code>git rebase B A</code>
(may need to rebase other descendant branches separately)</td>
(may need to rebase other descendant bookmarks separately)</td>
</tr>
<tr>
<td>Move change A and its descendants onto change B</td>
<td><code>jj rebase -s A -d B</code></td>
<td><code>git rebase --onto B A^ &lt;some descendant branch&gt;</code>
(may need to rebase other descendant branches separately)</td>
<td><code>git rebase --onto B A^ &lt;some descendant bookmark&gt;</code>
(may need to rebase other descendant bookmarks separately)</td>
</tr>
<tr>
<td>Reorder changes from A-B-C-D to A-C-B-D</td>
@ -324,29 +324,29 @@ parent.
<td><code>git rev-parse --show-toplevel</code></td>
</tr>
<tr>
<td>List branches</td>
<td><code>jj branch list</code></td>
<td><code>git branch</code></td>
<td>List bookmarks</td>
<td><code>jj bookmark list</code></td>
<td><code>git bookmark</code></td>
</tr>
<tr>
<td>Create a branch</td>
<td><code>jj branch create &lt;name&gt; -r &lt;revision&gt;</code></td>
<td><code>git branch &lt;name&gt; &lt;revision&gt;</code></td>
<td>Create a bookmark</td>
<td><code>jj bookmark create &lt;name&gt; -r &lt;revision&gt;</code></td>
<td><code>git bookmark &lt;name&gt; &lt;revision&gt;</code></td>
</tr>
<tr>
<td>Move a branch forward</td>
<td><code>jj branch set &lt;name&gt; -r &lt;revision&gt;</code></td>
<td><code>git branch -f &lt;name&gt; &lt;revision&gt;</code></td>
<td>Move a bookmark forward</td>
<td><code>jj bookmark set &lt;name&gt; -r &lt;revision&gt;</code></td>
<td><code>git bookmark -f &lt;name&gt; &lt;revision&gt;</code></td>
</tr>
<tr>
<td>Move a branch backward or sideways</td>
<td><code>jj branch set &lt;name&gt; -r &lt;revision&gt; --allow-backwards</code></td>
<td><code>git branch -f &lt;name&gt; &lt;revision&gt;</code></td>
<td>Move a bookmark backward or sideways</td>
<td><code>jj bookmark set &lt;name&gt; -r &lt;revision&gt; --allow-backwards</code></td>
<td><code>git bookmark -f &lt;name&gt; &lt;revision&gt;</code></td>
</tr>
<tr>
<td>Delete a branch</td>
<td><code>jj branch delete &lt;name&gt; </code></td>
<td><code>git branch --delete &lt;name&gt;</code></td>
<td>Delete a bookmark</td>
<td><code>jj bookmark delete &lt;name&gt; </code></td>
<td><code>git bookmark --delete &lt;name&gt;</code></td>
</tr>
<tr>
<td>See log of operations performed on the repo</td>

View file

@ -24,7 +24,7 @@ a comparison with Git, including how workflows are different, see the
only `~/.ssh/id_rsa`, `~/.ssh/id_ed25519` or `~/.ssh/id_ed25519_sk`), or
a `credential.helper`.
* **Branches: Yes.** You can read more about
[how branches work in Jujutsu](branches.md)
[how branches work in Jujutsu](bookmarks.md)
and [how they interoperate with Git](#branches).
* **Tags: Partial.** You can check out tagged commits by name (pointed to be
either annotated or lightweight tags), but you cannot create new tags.

View file

@ -12,54 +12,54 @@ authenticated HTTP.
## Basic workflow
The simplest way to start with Jujutsu is to create a stack of commits first.
You will only need to create a branch when you need to push the stack to a
remote. There are two primary workflows: using a generated branch name or
naming a branch.
You will only need to create a bookmark when you need to push the stack to a
remote. There are two primary workflows: using a generated bookmark name or
naming a bookmark.
### Using a generated branch name
### Using a generated bookmark name
In this example we're letting Jujutsu auto-create a branch.
In this example we're letting Jujutsu auto-create a bookmark.
```shell
# Start a new commit off of the default branch.
# Start a new commit off of the default bookmark.
$ jj new main
# Refactor some files, then add a description and start a new commit
$ jj commit -m 'refactor(foo): restructure foo()'
# Add a feature, then add a description and start a new commit
$ jj commit -m 'feat(bar): add support for bar'
# Let Jujutsu generate a branch name and push that to GitHub. Note that we
# Let Jujutsu generate a bookmark name and push that to GitHub. Note that we
# push the working-copy commit's *parent* because the working-copy commit
# itself is empty.
$ jj git push -c @-
```
### Using a named branch
### Using a named bookmark
In this example, we create a branch named `bar` and then push it to the remote.
In this example, we create a bookmark named `bar` and then push it to the remote.
```shell
# Start a new commit off of the default branch.
# Start a new commit off of the default bookmark.
$ jj new main
# Refactor some files, then add a description and start a new commit
$ jj commit -m 'refactor(foo): restructure foo()'
# Add a feature, then add a description and start a new commit
$ jj commit -m 'feat(bar): add support for bar'
# Create a branch so we can push it to GitHub. Note that we created the branch
# Create a bookmark so we can push it to GitHub. Note that we created the bookmark
# on the working-copy commit's *parent* because the working copy itself is empty.
$ jj branch create bar -r @- # `bar` now contains the previous two commits.
# Push the branch to GitHub (pushes only `bar`)
$ jj bookmark create bar -r @- # `bar` now contains the previous two commits.
# Push the bookmark to GitHub (pushes only `bar`)
$ jj git push
```
While it's possible to create a branch in advance and commit on top of it in a
Git-like manner, you will then need to move the branch manually when you create
While it's possible to create a bookmark in advance and commit on top of it in a
Git-like manner, you will then need to move the bookmark manually when you create
a new commits. Unlike Git, Jujutsu will not do it automatically.
## Updating the repository
As of October 2023, Jujutsu has no equivalent to a `git pull` command (see
[issue #1039][sync-issue]). Until such a command is added, you need to use
`jj git fetch` followed by a `jj rebase -d $main_branch` to update your
`jj git fetch` followed by a `jj rebase -d $main_bookmark` to update your
changes.
[sync-issue]: https://github.com/martinvonz/jj/issues/1039
@ -67,7 +67,7 @@ changes.
## Working in a Git co-located repository
After doing `jj git init --colocate`, Git will be in a [detached HEAD
state][detached], which is unusual, as Git mainly works with branches. In a
state][detached], which is unusual, as Git mainly works with bookmarks. In a
co-located repository, every `jj` command will automatically synchronize
Jujutsu's view of the repo with Git's view. For example, `jj commit` updates the
HEAD of the Git repository, enabling an incremental migration.
@ -76,21 +76,21 @@ HEAD of the Git repository, enabling an incremental migration.
$ nvim docs/tutorial.md
$ # Do some more work.
$ jj commit -m "Update tutorial"
# Create a branch on the working-copy commit's parent
$ jj branch create doc-update -r @-
# Create a bookmark on the working-copy commit's parent
$ jj bookmark create doc-update -r @-
$ jj git push
```
## Working in a Jujutsu repository
In a Jujutsu repository, the workflow is simplified. If there's no need for
explicitly named branches, you can just generate one for a change. As Jujutsu is
able to create a branch for a revision.
explicitly named bookmarks, you can just generate one for a change. As Jujutsu is
able to create a bookmark for a revision.
```shell
$ # Do your work
$ jj commit
$ # Push change "mw", letting Jujutsu automatically create a branch called
$ # Push change "mw", letting Jujutsu automatically create a bookmark called
$ # "push-mwmpwkwknuz"
$ jj git push --change mw
```
@ -99,7 +99,7 @@ $ jj git push --change mw
There are two workflows for addressing review comments, depending on your
project's preference. Many projects prefer that you address comments by adding
commits to your branch[^1]. Some projects (such as Jujutsu and LLVM) instead
commits to your bookmark[^1]. Some projects (such as Jujutsu and LLVM) instead
prefer that you keep your commits clean by rewriting them and then
force-pushing[^2].
@ -109,14 +109,14 @@ If your project prefers that you address review comments by adding commits on
top, you can do that by doing something like this:
```shell
$ # Create a new commit on top of the `your-feature` branch from above.
$ # Create a new commit on top of the `your-feature` bookmark from above.
$ jj new your-feature
$ # Address the comments by updating the code. Then review the changes.
$ jj diff
$ # Give the fix a description and create a new working-copy on top.
$ jj commit -m 'address pr comments'
$ # Update the branch to point to the new commit.
$ jj branch set your-feature -r @-
$ # Update the bookmark to point to the new commit.
$ jj bookmark set your-feature -r @-
$ # Push it to your remote
$ jj git push
```
@ -129,14 +129,14 @@ achieved without creating a new commit.
> still get amended to the previous commit.
```shell
$ # Create a new commit on top of the `your-feature` branch from above.
$ # Create a new commit on top of the `your-feature` bookmark from above.
$ jj new your-feature
$ # Address the comments by updating the code. Then review the changes.
$ jj diff
$ # Give the fix a description.
$ jj describe -m 'address pr comments'
$ # Update the branch to point to the current commit.
$ jj branch set your-feature -r @
$ # Update the bookmark to point to the current commit.
$ jj bookmark set your-feature -r @
$ # Push it to your remote
$ jj git push
```
@ -154,26 +154,26 @@ $ # Address the comments by updating the code. Then review the changes.
$ jj diff
$ # Squash the changes into the parent commit
$ jj squash
$ # Push the updated branch to the remote. Jujutsu automatically makes it a
$ # Push the updated bookmark to the remote. Jujutsu automatically makes it a
$ # force push
$ jj git push --branch your-feature
$ jj git push --bookmark your-feature
```
The hyphen after `your-feature` comes from the
[revset](https://github.com/martinvonz/jj/blob/main/docs/revsets.md) syntax.
## Working with other people's branches
## Working with other people's bookmarks
By default, `jj git clone` imports the default remote branch (which is usually
`main` or `master`), but `jj git fetch` doesn't import new remote branches to
local branches. This means that if you want to iterate or test another
contributor's branch, you'll need to do `jj new <branch>@<remote>` onto it.
By default, `jj git clone` imports the default remote bookmark (which is usually
`main` or `master`), but `jj git fetch` doesn't import new remote bookmarks to
local bookmarks. This means that if you want to iterate or test another
contributor's bookmark, you'll need to do `jj new <bookmark>@<remote>` onto it.
If you want to import all remote branches including inactive ones, set
`git.auto-local-branch = true` in the config file. Then you can specify a
contributor's branch as `jj new <branch>` instead of `jj new <branch>@<remote>`.
If you want to import all remote bookmarks including inactive ones, set
`git.auto-local-bookmark = true` in the config file. Then you can specify a
contributor's bookmark as `jj new <bookmark>` instead of `jj new <bookmark>@<remote>`.
You can find more information on that setting [here][auto-branch].
You can find more information on that setting [here][auto-bookmark].
## Using GitHub CLI
@ -202,30 +202,30 @@ commands like `gh issue list` normally.
## Useful Revsets
Log all revisions across all local branches that aren't on the main branch nor
Log all revisions across all local bookmarks that aren't on the main bookmark nor
on any remote:
```shell
$ jj log -r 'branches() & ~(main | remote_branches())'
$ jj log -r 'bookmarks() & ~(main | remote_bookmarks())'
```
Log all revisions that you authored, across all branches that aren't on any
Log all revisions that you authored, across all bookmarks that aren't on any
remote:
```shell
$ jj log -r 'mine() & branches() & ~remote_branches()'
$ jj log -r 'mine() & bookmarks() & ~remote_bookmarks()'
```
Log all remote branches that you authored or committed to:
Log all remote bookmarks that you authored or committed to:
```shell
$ jj log -r 'remote_branches() & (mine() | committer(your@email.com))'
$ jj log -r 'remote_bookmarks() & (mine() | committer(your@email.com))'
```
Log all descendants of the current working copy that aren't on any remote:
```shell
$ jj log -r '::@ & ~remote_branches()'
$ jj log -r '::@ & ~remote_bookmarks()'
```
## Merge conflicts
@ -235,13 +235,13 @@ the [tutorial][tut].
[^1]:
This is a GitHub-style review, as GitHub currently only is able to compare
branches.
bookmarks.
[^2]:
If you're wondering why we prefer clean commits in this project, see
e.g. [this blog post][stacked]
[auto-branch]: config.md#automatic-local-branch-creation
[auto-bookmark]: config.md#automatic-local-bookmark-creation
[detached]: https://git-scm.com/docs/git-checkout#_detached_head
[gh]: https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent
[http-auth]: https://github.com/martinvonz/jj/issues/469

View file

@ -1,12 +1,12 @@
# Glossary
## Anonymous branch
## Anonymous bookmark
An anonymous branch is a chain of commits that doesn't have any
[named branches](#branch) pointing to it or to any of its descendants. Unlike
Git, Jujutsu keeps commits on anonymous branches around until they are
explicitly abandoned. Visible anonymous branches are tracked by the
[view](#view), which stores a list of [heads](#head) of such branches.
An anonymous bookmark is a chain of commits that doesn't have any
[named bookmarks](#bookmark) pointing to it or to any of its descendants. Unlike
Git, Jujutsu keeps commits on anonymous bookmarks around until they are
explicitly abandoned. Visible anonymous bookmarks are tracked by the
[view](#view), which stores a list of [heads](#head) of such bookmarks.
## Backend
@ -20,15 +20,15 @@ There are also pluggable backends for storing other information than commits,
such as the "operation store backend" for storing
[the operation log](#operation-log).
## Branch
## Bookmark
A branch is a named pointer to a [commit](#commit). They automatically follow
A bookmark is a named pointer to a [commit](#commit). They automatically follow
the commit if it gets [rewritten](#rewrite). Branches are sometimes called
"named branches" to distinguish them from
[anonymous branches](#anonymous-branch), but note that they are more similar
to Git's branches than to
[Mercurial's named branches](https://www.mercurial-scm.org/wiki/Branch#Named_branches).
See [here](branches.md) for details.
"named bookmarks" to distinguish them from
[anonymous bookmarks](#anonymous-bookmark), but note that they are more similar
to Git's bookmarks than to
[Mercurial's named bookmarks](https://www.mercurial-scm.org/wiki/Branch#Named_bookmarks).
See [here](bookmarks.md) for details.
## Change
@ -85,12 +85,12 @@ Those are the conflicts that users coming from other VCSs are usually familiar
with. You can see them in `jj status` and in `jj log` (the red "conflict"
label at the end of the line). See [here](conflicts.md) for details.
Conflicts can also occur in [branches](#branch). For example, if you moved a
branch locally, and it was also moved on the remote, then the branch will be
Conflicts can also occur in [bookmarks](#bookmark). For example, if you moved a
bookmark locally, and it was also moved on the remote, then the bookmark will be
in a conflicted state after you pull from the remote.
See [here](branches.md#conflicts) for details.
See [here](bookmarks.md#conflicts) for details.
Similar to a branch conflict, when a [change](#change) is rewritten locally
Similar to a bookmark conflict, when a [change](#change) is rewritten locally
and remotely, for example, then the change will be in a conflicted state. We
call that a [divergent change](#divergent-change).
@ -105,7 +105,7 @@ A head is a commit with no descendants. The context in which it has no
descendants varies. For example, the `heads(X)`
[revset function](revsets.md#functions) returns commits that have no descendants
within the set `X` itself. The [view](#view) records which
anonymous heads (heads without a branch pointing to them) are visible at a
anonymous heads (heads without a bookmark pointing to them) are visible at a
given [operation](#operation). Note that this is quite different from Git's
[HEAD](https://git-scm.com/book/en/v2/Git-Internals-Git-References#ref_the_ref).
@ -115,7 +115,7 @@ See [visible commits](#visible-commits).
## Operation
A snapshot of the [visible commits](#visible-commits) and [branches](#branch)
A snapshot of the [visible commits](#visible-commits) and [bookmarks](#bookmark)
at a given point in time (technically a [view object](#view)), together with
some metadata. The metadata includes the username, hostname, timestamps, and
pointers to the operation's parents.
@ -173,12 +173,12 @@ A tree object represents a snapshot of a directory in the repository. Tree
objects are defined recursively; each tree object only has the files and
directories contained directly in the directory it represents.
## Tracked branches and tracking branches
## Tracked bookmarks and tracking bookmarks
A remote branch can be made "tracked" with the `jj branch track` command. This
results in a "tracking" local branch that tracks the remote branch.
A remote bookmark can be made "tracked" with the `jj bookmark track` command. This
results in a "tracking" local bookmark that tracks the remote bookmark.
See [the branches documentation](branches.md#terminology-summary) for a more
See [the bookmarks documentation](bookmarks.md#terminology-summary) for a more
detailed definition of these terms.
## Visible commits
@ -195,7 +195,7 @@ accessible by their [commit id](#commit-id).
## View
A view is a snapshot of branches and their targets, anonymous heads,
A view is a snapshot of bookmarks and their targets, anonymous heads,
and working-copy commits. The anonymous heads define which commits
are [visible](#visible-commits).

View file

@ -80,11 +80,11 @@ This type cannot be printed. The following methods are defined.
working-copy commit as `<workspace name>@`.
* `current_working_copy() -> Boolean`: True for the working-copy commit of the
current workspace.
* `branches() -> List<RefName>`: Local and remote branches pointing to the commit.
A tracking remote branch will be included only if its target is different
from the local one.
* `local_branches() -> List<RefName>`: All local branches pointing to the commit.
* `remote_branches() -> List<RefName>`: All remote branches pointing to the commit.
* `bookmarks() -> List<RefName>`: Local and remote bookmarks pointing to the
commit. A tracking remote branch will be included only if its target is
different from the local one.
* `local_bookmarks() -> List<RefName>`: All local bookmarks pointing to the commit.
* `remote_bookmarks() -> List<RefName>`: All remote bookmarks pointing to the commit.
* `tags() -> List<RefName>`
* `git_refs() -> List<RefName>`
* `git_head() -> Option<RefName>`
@ -162,7 +162,7 @@ The following methods are defined.
* `.remote() -> String`: Remote name or empty if this is a local ref.
* `.present() -> Boolean`: True if the ref points to any commit.
* `.conflict() -> Boolean`: True if [the branch or tag is
conflicted](branches.md#conflicts).
conflicted](bookmarks.md#conflicts).
* `.normal_target() -> Option<Commit>`: Target commit if the ref is not
conflicted and points to a commit.
* `.removed_targets() -> List<Commit>`: Old target commits if conflicted.

View file

@ -171,7 +171,7 @@ where
/// For example, topological order of chronological data should respect
/// timestamp (except a few outliers caused by clock skew.)
///
/// Use `topo_order_reverse()` if the DAG is heavily branched. This can
/// Use `topo_order_reverse()` if the DAG is heavily bookmarked. This can
/// only process linear part lazily.
pub fn topo_order_reverse_lazy<T, ID, II, NI>(
start: II,
@ -277,7 +277,7 @@ impl<T: Ord, ID: Hash + Eq + Clone, E> TopoOrderReverseLazyInner<T, ID, E> {
}
}
/// Splits DAG at single fork point, and extracts branchy part as sub graph.
/// Splits DAG at single fork point, and extracts bookmarky part as sub graph.
///
/// ```text
/// o | C
@ -286,9 +286,9 @@ impl<T: Ord, ID: Hash + Eq + Clone, E> TopoOrderReverseLazyInner<T, ID, E> {
/// o A
/// ```
///
/// If a branch reached to root (empty neighbors), the graph can't be split
/// anymore because the other branch may be connected to a descendant of
/// the rooted branch.
/// If a bookmark reached to root (empty neighbors), the graph can't be split
/// anymore because the other bookmark may be connected to a descendant of
/// the rooted bookmark.
///
/// ```text
/// o | C
@ -809,7 +809,7 @@ mod tests {
assert_eq!(common, vec!['E', 'D', 'C', 'B', 'a']);
// The root node 'a' is visited before 'C'. If the graph were split there,
// the branch 'C->B->a' would be orphaned.
// the bookmark 'C->B->a' would be orphaned.
let common = topo_order_reverse_lazy(vec!['E'], id_fn, neighbors_fn).collect_vec();
assert_eq!(common, vec!['E', 'D', 'C', 'B', 'a']);
@ -1067,7 +1067,7 @@ mod tests {
}
#[test]
fn test_topo_order_reverse_cycle_to_branchy_sub_graph() {
fn test_topo_order_reverse_cycle_to_bookmarky_sub_graph() {
// This graph:
// o D
// |\

View file

@ -312,18 +312,22 @@ pub fn import_some_refs(
match ref_name {
RefName::LocalBranch(branch) => {
if new_remote_ref.is_tracking() {
mut_repo.merge_local_branch(branch, base_target, &new_remote_ref.target);
mut_repo.merge_local_bookmark(branch, base_target, &new_remote_ref.target);
}
// Update Git-tracking branch like the other remote branches.
mut_repo.set_remote_branch(branch, REMOTE_NAME_FOR_LOCAL_GIT_REPO, new_remote_ref);
mut_repo.set_remote_bookmark(
branch,
REMOTE_NAME_FOR_LOCAL_GIT_REPO,
new_remote_ref,
);
}
RefName::RemoteBranch { branch, remote } => {
if new_remote_ref.is_tracking() {
mut_repo.merge_local_branch(branch, base_target, &new_remote_ref.target);
mut_repo.merge_local_bookmark(branch, base_target, &new_remote_ref.target);
}
// Remote-tracking branch is the last known state of the branch in the remote.
// It shouldn't diverge even if we had inconsistent view.
mut_repo.set_remote_branch(branch, remote, new_remote_ref);
mut_repo.set_remote_bookmark(branch, remote, new_remote_ref);
}
RefName::Tag(name) => {
if new_remote_ref.is_tracking() {
@ -400,7 +404,7 @@ fn diff_refs_to_import(
.collect();
// TODO: migrate tags to the remote view, and don't destructure &RemoteRef
let mut known_remote_refs: HashMap<RefName, (&RefTarget, RemoteRefState)> = itertools::chain(
view.all_remote_branches()
view.all_remote_bookmarks()
.map(|((branch, remote), remote_ref)| {
// TODO: want to abstract local ref as "git" tracking remote, but
// we'll probably need to refactor the git_ref_filter API first.
@ -496,7 +500,7 @@ fn default_remote_ref_state_for(ref_name: &RefName, git_settings: &GitSettings)
// LocalBranch means Git-tracking branch
RefName::LocalBranch(_) | RefName::Tag(_) => RemoteRefState::Tracking,
RefName::RemoteBranch { .. } => {
if git_settings.auto_local_branch {
if git_settings.auto_local_bookmark {
RemoteRefState::Tracking
} else {
RemoteRefState::New
@ -512,7 +516,7 @@ fn default_remote_ref_state_for(ref_name: &RefName, git_settings: &GitSettings)
/// tracking remotes, and such mutation isn't applied to `view.git_refs()` yet.
fn pinned_commit_ids(view: &View) -> Vec<CommitId> {
itertools::chain(
view.local_branches().map(|(_, target)| target),
view.local_bookmarks().map(|(_, target)| target),
view.tags().values(),
)
.flat_map(|target| target.added_ids())
@ -527,7 +531,7 @@ fn pinned_commit_ids(view: &View) -> Vec<CommitId> {
/// propagate to the other remotes on later push. OTOH, untracked remote
/// branches are considered independent refs.
fn remotely_pinned_commit_ids(view: &View) -> Vec<CommitId> {
view.all_remote_branches()
view.all_remote_bookmarks()
.filter(|(_, remote_ref)| !remote_ref.is_tracking())
.map(|(_, remote_ref)| &remote_ref.target)
.flat_map(|target| target.added_ids())
@ -747,7 +751,7 @@ fn copy_exportable_local_branches_to_remote_view(
) {
let new_local_branches = mut_repo
.view()
.local_remote_branches(remote_name)
.local_remote_bookmarks(remote_name)
.filter_map(|(branch, targets)| {
// TODO: filter out untracked branches (if we add support for untracked @git
// branches)
@ -763,7 +767,7 @@ fn copy_exportable_local_branches_to_remote_view(
target: new_target,
state: RemoteRefState::Tracking,
};
mut_repo.set_remote_branch(&branch, remote_name, new_remote_ref);
mut_repo.set_remote_bookmark(&branch, remote_name, new_remote_ref);
}
}
@ -776,9 +780,9 @@ fn diff_refs_to_export(
// Local targets will be copied to the "git" remote if successfully exported. So
// the local branches are considered to be the new "git" remote branches.
let mut all_branch_targets: HashMap<RefName, (&RefTarget, &RefTarget)> = itertools::chain(
view.local_branches()
view.local_bookmarks()
.map(|(branch, target)| (RefName::LocalBranch(branch.to_owned()), target)),
view.all_remote_branches()
view.all_remote_bookmarks()
.filter(|&((_, remote), _)| remote != REMOTE_NAME_FOR_LOCAL_GIT_REPO)
.map(|((branch, remote), remote_ref)| {
let ref_name = RefName::RemoteBranch {
@ -1379,7 +1383,7 @@ pub fn push_branches(
state: RemoteRefState::Tracking,
};
mut_repo.set_git_ref_target(&git_ref_name, new_remote_ref.target.clone());
mut_repo.set_remote_branch(branch_name, remote_name, new_remote_ref);
mut_repo.set_remote_bookmark(branch_name, remote_name, new_remote_ref);
}
Ok(())

View file

@ -152,7 +152,7 @@ impl RefTarget {
}
}
/// Remote branch or tag.
/// Remote bookmark or tag.
#[derive(ContentHash, Clone, Debug, Eq, Hash, PartialEq)]
pub struct RemoteRef {
pub target: RefTarget,
@ -253,10 +253,10 @@ impl<'a> RefTargetOptionExt for Option<&'a RemoteRef> {
}
}
/// Local and remote branches of the same branch name.
/// Local and remote bookmarks of the same bookmark name.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct BranchTarget<'a> {
/// The commit the branch points to locally.
/// The commit the bookmark points to locally.
pub local_target: &'a RefTarget,
/// `(remote_name, remote_ref)` pairs in lexicographical order.
pub remote_refs: Vec<(&'a str, &'a RemoteRef)>,
@ -268,13 +268,13 @@ pub struct BranchTarget<'a> {
pub struct View {
/// All head commits
pub head_ids: HashSet<CommitId>,
pub local_branches: BTreeMap<String, RefTarget>,
pub local_bookmarks: BTreeMap<String, RefTarget>,
pub tags: BTreeMap<String, RefTarget>,
pub remote_views: BTreeMap<String, RemoteView>,
pub git_refs: BTreeMap<String, RefTarget>,
/// The commit the Git HEAD points to.
// TODO: Support multiple Git worktrees?
// TODO: Do we want to store the current branch name too?
// TODO: Do we want to store the current bookmark name too?
pub git_head: RefTarget,
// The commit that *should be* checked out in the workspace. Note that the working copy
// (.jj/working_copy/) has the source of truth about which commit *is* checked out (to be
@ -285,60 +285,63 @@ pub struct View {
/// Represents the state of the remote repo.
#[derive(ContentHash, Clone, Debug, Default, Eq, PartialEq)]
pub struct RemoteView {
// TODO: Do we need to support tombstones for remote branches? For example, if the branch
// TODO: Do we need to support tombstones for remote bookmarks? For example, if the bookmark
// has been deleted locally and you pull from a remote, maybe it should make a difference
// whether the branch is known to have existed on the remote. We may not want to resurrect
// the branch if the branch's state on the remote was just not known.
pub branches: BTreeMap<String, RemoteRef>,
// whether the bookmark is known to have existed on the remote. We may not want to resurrect
// the bookmark if the bookmark's state on the remote was just not known.
pub bookmarks: BTreeMap<String, RemoteRef>,
// TODO: pub tags: BTreeMap<String, RemoteRef>,
}
/// Iterates pair of local and remote branches by branch name.
pub(crate) fn merge_join_branch_views<'a>(
local_branches: &'a BTreeMap<String, RefTarget>,
/// Iterates pair of local and remote bookmarks by bookmark name.
pub(crate) fn merge_join_bookmark_views<'a>(
local_bookmarks: &'a BTreeMap<String, RefTarget>,
remote_views: &'a BTreeMap<String, RemoteView>,
) -> impl Iterator<Item = (&'a str, BranchTarget<'a>)> {
let mut local_branches_iter = local_branches
let mut local_bookmarks_iter = local_bookmarks
.iter()
.map(|(branch_name, target)| (branch_name.as_str(), target))
.map(|(bookmark_name, target)| (bookmark_name.as_str(), target))
.peekable();
let mut remote_branches_iter = flatten_remote_branches(remote_views).peekable();
let mut remote_bookmarks_iter = flatten_remote_bookmarks(remote_views).peekable();
iter::from_fn(move || {
// Pick earlier branch name
let (branch_name, local_target) =
if let Some(&((remote_branch_name, _), _)) = remote_branches_iter.peek() {
local_branches_iter
.next_if(|&(local_branch_name, _)| local_branch_name <= remote_branch_name)
.unwrap_or((remote_branch_name, RefTarget::absent_ref()))
} else {
local_branches_iter.next()?
};
let remote_refs = remote_branches_iter
.peeking_take_while(|&((remote_branch_name, _), _)| remote_branch_name == branch_name)
// Pick earlier bookmark name
let (bookmark_name, local_target) = if let Some(&((remote_bookmark_name, _), _)) =
remote_bookmarks_iter.peek()
{
local_bookmarks_iter
.next_if(|&(local_bookmark_name, _)| local_bookmark_name <= remote_bookmark_name)
.unwrap_or((remote_bookmark_name, RefTarget::absent_ref()))
} else {
local_bookmarks_iter.next()?
};
let remote_refs = remote_bookmarks_iter
.peeking_take_while(|&((remote_bookmark_name, _), _)| {
remote_bookmark_name == bookmark_name
})
.map(|((_, remote_name), remote_ref)| (remote_name, remote_ref))
.collect();
let branch_target = BranchTarget {
let bookmark_target = BranchTarget {
local_target,
remote_refs,
};
Some((branch_name, branch_target))
Some((bookmark_name, bookmark_target))
})
}
/// Iterates branch `((name, remote_name), remote_ref)`s in lexicographical
/// Iterates bookmark `((name, remote_name), remote_ref)`s in lexicographical
/// order.
pub(crate) fn flatten_remote_branches(
pub(crate) fn flatten_remote_bookmarks(
remote_views: &BTreeMap<String, RemoteView>,
) -> impl Iterator<Item = ((&str, &str), &RemoteRef)> {
remote_views
.iter()
.map(|(remote_name, remote_view)| {
remote_view
.branches
.bookmarks
.iter()
.map(move |(branch_name, remote_ref)| {
let full_name = (branch_name.as_str(), remote_name.as_str());
.map(move |(bookmark_name, remote_ref)| {
let full_name = (bookmark_name.as_str(), remote_name.as_str());
(full_name, remote_ref)
})
})
@ -464,62 +467,62 @@ mod tests {
use super::*;
#[test]
fn test_merge_join_branch_views() {
fn test_merge_join_bookmark_views() {
let remote_ref = |target: &RefTarget| RemoteRef {
target: target.clone(),
state: RemoteRefState::Tracking, // doesn't matter
};
let local_branch1_target = RefTarget::normal(CommitId::from_hex("111111"));
let local_branch2_target = RefTarget::normal(CommitId::from_hex("222222"));
let git_branch1_remote_ref = remote_ref(&RefTarget::normal(CommitId::from_hex("333333")));
let git_branch2_remote_ref = remote_ref(&RefTarget::normal(CommitId::from_hex("444444")));
let remote1_branch1_remote_ref =
let local_bookmark1_target = RefTarget::normal(CommitId::from_hex("111111"));
let local_bookmark2_target = RefTarget::normal(CommitId::from_hex("222222"));
let git_bookmark1_remote_ref = remote_ref(&RefTarget::normal(CommitId::from_hex("333333")));
let git_bookmark2_remote_ref = remote_ref(&RefTarget::normal(CommitId::from_hex("444444")));
let remote1_bookmark1_remote_ref =
remote_ref(&RefTarget::normal(CommitId::from_hex("555555")));
let remote2_branch2_remote_ref =
let remote2_bookmark2_remote_ref =
remote_ref(&RefTarget::normal(CommitId::from_hex("666666")));
let local_branches = btreemap! {
"branch1".to_owned() => local_branch1_target.clone(),
"branch2".to_owned() => local_branch2_target.clone(),
let local_bookmarks = btreemap! {
"bookmark1".to_owned() => local_bookmark1_target.clone(),
"bookmark2".to_owned() => local_bookmark2_target.clone(),
};
let remote_views = btreemap! {
"git".to_owned() => RemoteView {
branches: btreemap! {
"branch1".to_owned() => git_branch1_remote_ref.clone(),
"branch2".to_owned() => git_branch2_remote_ref.clone(),
bookmarks: btreemap! {
"bookmark1".to_owned() => git_bookmark1_remote_ref.clone(),
"bookmark2".to_owned() => git_bookmark2_remote_ref.clone(),
},
},
"remote1".to_owned() => RemoteView {
branches: btreemap! {
"branch1".to_owned() => remote1_branch1_remote_ref.clone(),
bookmarks: btreemap! {
"bookmark1".to_owned() => remote1_bookmark1_remote_ref.clone(),
},
},
"remote2".to_owned() => RemoteView {
branches: btreemap! {
"branch2".to_owned() => remote2_branch2_remote_ref.clone(),
bookmarks: btreemap! {
"bookmark2".to_owned() => remote2_bookmark2_remote_ref.clone(),
},
},
};
assert_eq!(
merge_join_branch_views(&local_branches, &remote_views).collect_vec(),
merge_join_bookmark_views(&local_bookmarks, &remote_views).collect_vec(),
vec![
(
"branch1",
"bookmark1",
BranchTarget {
local_target: &local_branch1_target,
local_target: &local_bookmark1_target,
remote_refs: vec![
("git", &git_branch1_remote_ref),
("remote1", &remote1_branch1_remote_ref),
("git", &git_bookmark1_remote_ref),
("remote1", &remote1_bookmark1_remote_ref),
],
},
),
(
"branch2",
"bookmark2",
BranchTarget {
local_target: &local_branch2_target.clone(),
local_target: &local_bookmark2_target.clone(),
remote_refs: vec![
("git", &git_branch2_remote_ref),
("remote2", &remote2_branch2_remote_ref),
("git", &git_bookmark2_remote_ref),
("remote2", &remote2_bookmark2_remote_ref),
],
},
),
@ -527,37 +530,37 @@ mod tests {
);
// Local only
let local_branches = btreemap! {
"branch1".to_owned() => local_branch1_target.clone(),
let local_bookmarks = btreemap! {
"bookmark1".to_owned() => local_bookmark1_target.clone(),
};
let remote_views = btreemap! {};
assert_eq!(
merge_join_branch_views(&local_branches, &remote_views).collect_vec(),
merge_join_bookmark_views(&local_bookmarks, &remote_views).collect_vec(),
vec![(
"branch1",
"bookmark1",
BranchTarget {
local_target: &local_branch1_target,
local_target: &local_bookmark1_target,
remote_refs: vec![],
},
)],
);
// Remote only
let local_branches = btreemap! {};
let local_bookmarks = btreemap! {};
let remote_views = btreemap! {
"remote1".to_owned() => RemoteView {
branches: btreemap! {
"branch1".to_owned() => remote1_branch1_remote_ref.clone(),
bookmarks: btreemap! {
"bookmark1".to_owned() => remote1_bookmark1_remote_ref.clone(),
},
},
};
assert_eq!(
merge_join_branch_views(&local_branches, &remote_views).collect_vec(),
merge_join_bookmark_views(&local_bookmarks, &remote_views).collect_vec(),
vec![(
"branch1",
"bookmark1",
BranchTarget {
local_target: RefTarget::absent_ref(),
remote_refs: vec![("remote1", &remote1_branch1_remote_ref)],
remote_refs: vec![("remote1", &remote1_bookmark1_remote_ref)],
},
)],
);

View file

@ -190,8 +190,8 @@ pub enum BranchPushAction {
}
/// Figure out what changes (if any) need to be made to the remote when pushing
/// this branch.
pub fn classify_branch_push_action(targets: LocalAndRemoteRef) -> BranchPushAction {
/// this bookmark.
pub fn classify_bookmark_push_action(targets: LocalAndRemoteRef) -> BranchPushAction {
let local_target = targets.local_target;
let remote_target = targets.remote_ref.tracking_target();
if local_target == remote_target {
@ -230,27 +230,27 @@ mod tests {
}
#[test]
fn test_classify_branch_push_action_unchanged() {
fn test_classify_bookmark_push_action_unchanged() {
let commit_id1 = CommitId::from_hex("11");
let targets = LocalAndRemoteRef {
local_target: &RefTarget::normal(commit_id1.clone()),
remote_ref: &tracking_remote_ref(RefTarget::normal(commit_id1)),
};
assert_eq!(
classify_branch_push_action(targets),
classify_bookmark_push_action(targets),
BranchPushAction::AlreadyMatches
);
}
#[test]
fn test_classify_branch_push_action_added() {
fn test_classify_bookmark_push_action_added() {
let commit_id1 = CommitId::from_hex("11");
let targets = LocalAndRemoteRef {
local_target: &RefTarget::normal(commit_id1.clone()),
remote_ref: RemoteRef::absent_ref(),
};
assert_eq!(
classify_branch_push_action(targets),
classify_bookmark_push_action(targets),
BranchPushAction::Update(BranchPushUpdate {
old_target: None,
new_target: Some(commit_id1),
@ -259,14 +259,14 @@ mod tests {
}
#[test]
fn test_classify_branch_push_action_removed() {
fn test_classify_bookmark_push_action_removed() {
let commit_id1 = CommitId::from_hex("11");
let targets = LocalAndRemoteRef {
local_target: RefTarget::absent_ref(),
remote_ref: &tracking_remote_ref(RefTarget::normal(commit_id1.clone())),
};
assert_eq!(
classify_branch_push_action(targets),
classify_bookmark_push_action(targets),
BranchPushAction::Update(BranchPushUpdate {
old_target: Some(commit_id1),
new_target: None,
@ -275,7 +275,7 @@ mod tests {
}
#[test]
fn test_classify_branch_push_action_updated() {
fn test_classify_bookmark_push_action_updated() {
let commit_id1 = CommitId::from_hex("11");
let commit_id2 = CommitId::from_hex("22");
let targets = LocalAndRemoteRef {
@ -283,7 +283,7 @@ mod tests {
remote_ref: &tracking_remote_ref(RefTarget::normal(commit_id1.clone())),
};
assert_eq!(
classify_branch_push_action(targets),
classify_bookmark_push_action(targets),
BranchPushAction::Update(BranchPushUpdate {
old_target: Some(commit_id1),
new_target: Some(commit_id2),
@ -292,22 +292,22 @@ mod tests {
}
#[test]
fn test_classify_branch_push_action_removed_untracked() {
// This is not RemoteUntracked error since non-tracking remote branches
// have no relation to local branches, and there's nothing to push.
fn test_classify_bookmark_push_action_removed_untracked() {
// This is not RemoteUntracked error since non-tracking remote bookmarks
// have no relation to local bookmarks, and there's nothing to push.
let commit_id1 = CommitId::from_hex("11");
let targets = LocalAndRemoteRef {
local_target: RefTarget::absent_ref(),
remote_ref: &new_remote_ref(RefTarget::normal(commit_id1.clone())),
};
assert_eq!(
classify_branch_push_action(targets),
classify_bookmark_push_action(targets),
BranchPushAction::AlreadyMatches
);
}
#[test]
fn test_classify_branch_push_action_updated_untracked() {
fn test_classify_bookmark_push_action_updated_untracked() {
let commit_id1 = CommitId::from_hex("11");
let commit_id2 = CommitId::from_hex("22");
let targets = LocalAndRemoteRef {
@ -315,13 +315,13 @@ mod tests {
remote_ref: &new_remote_ref(RefTarget::normal(commit_id1.clone())),
};
assert_eq!(
classify_branch_push_action(targets),
classify_bookmark_push_action(targets),
BranchPushAction::RemoteUntracked
);
}
#[test]
fn test_classify_branch_push_action_local_conflicted() {
fn test_classify_bookmark_push_action_local_conflicted() {
let commit_id1 = CommitId::from_hex("11");
let commit_id2 = CommitId::from_hex("22");
let targets = LocalAndRemoteRef {
@ -329,13 +329,13 @@ mod tests {
remote_ref: &tracking_remote_ref(RefTarget::normal(commit_id1)),
};
assert_eq!(
classify_branch_push_action(targets),
classify_bookmark_push_action(targets),
BranchPushAction::LocalConflicted
);
}
#[test]
fn test_classify_branch_push_action_remote_conflicted() {
fn test_classify_bookmark_push_action_remote_conflicted() {
let commit_id1 = CommitId::from_hex("11");
let commit_id2 = CommitId::from_hex("22");
let targets = LocalAndRemoteRef {
@ -346,7 +346,7 @@ mod tests {
)),
};
assert_eq!(
classify_branch_push_action(targets),
classify_bookmark_push_action(targets),
BranchPushAction::RemoteConflicted
);
}

View file

@ -919,7 +919,7 @@ impl MutableRepo {
/// Record a commit as being rewritten into multiple other commits in this
/// transaction.
///
/// A later call to `rebase_descendants()` will update branches pointing to
/// A later call to `rebase_descendants()` will update bookmarks pointing to
/// `old_id` be conflicted and pointing to all pf `new_ids`. Working copies
/// pointing to `old_id` will be updated to point to the first commit in
/// `new_ids``. Descendants of `old_id` will be left alone.
@ -939,10 +939,10 @@ impl MutableRepo {
///
/// This record is used by `rebase_descendants` to know which commits have
/// children that need to be rebased, and where to rebase the children (as
/// well as branches) to.
/// well as bookmarks) to.
///
/// The `rebase_descendants` logic will rebase the descendants of `old_id`
/// to become the descendants of parent(s) of `old_id`. Any branches at
/// to become the descendants of parent(s) of `old_id`. Any bookmarks at
/// `old_id` would be moved to the parent(s) of `old_id` as well.
// TODO: Propagate errors from commit lookup or take a Commit as argument.
pub fn record_abandoned_commit(&mut self, old_id: CommitId) {
@ -1056,7 +1056,7 @@ impl MutableRepo {
new_mapping
}
/// Updates branches, working copies, and anonymous heads after rewriting
/// Updates bookmarks, working copies, and anonymous heads after rewriting
/// and/or abandoning commits.
pub fn update_rewritten_references(&mut self, settings: &UserSettings) -> BackendResult<()> {
self.update_all_references(settings)?;
@ -1074,7 +1074,7 @@ impl MutableRepo {
fn update_local_branches(&mut self, rewrite_mapping: &HashMap<CommitId, Vec<CommitId>>) {
let changed_branches = self
.view()
.local_branches()
.local_bookmarks()
.flat_map(|(name, target)| {
target.added_ids().filter_map(|id| {
let change = rewrite_mapping.get_key_value(id)?;
@ -1082,7 +1082,7 @@ impl MutableRepo {
})
})
.collect_vec();
for (branch_name, (old_commit_id, new_commit_ids)) in changed_branches {
for (bookmark_name, (old_commit_id, new_commit_ids)) in changed_branches {
let old_target = RefTarget::normal(old_commit_id.clone());
let new_target = RefTarget::from_merge(
MergeBuilder::from_iter(
@ -1091,7 +1091,8 @@ impl MutableRepo {
)
.build(),
);
self.merge_local_branch(&branch_name, &old_target, &new_target);
self.merge_local_bookmark(&bookmark_name, &old_target, &new_target);
}
}
@ -1268,7 +1269,7 @@ impl MutableRepo {
Ok(Some(rebaser))
}
// TODO(ilyagr): Either document that this also moves branches (rename the
// TODO(ilyagr): Either document that this also moves bookmarks (rename the
// function and the related functions?) or change things so that this only
// rebases descendants.
pub fn rebase_descendants_with_options(
@ -1295,7 +1296,7 @@ impl MutableRepo {
/// key-value pair where the key is the abandoned commit and the value is
/// **its parent**. One can tell this case apart since the change ids of the
/// key and the value will not match. The parent will inherit the
/// descendants and the branches of the abandoned commit.
/// descendants and the bookmarks of the abandoned commit.
// TODO: Rewrite this using `transform_descendants()`
pub fn rebase_descendants_with_options_return_map(
&mut self,
@ -1390,7 +1391,7 @@ impl MutableRepo {
.filter(|&(ws_id, _)| ws_id != workspace_id)
.map(|(_, wc_id)| wc_id)
.chain(
view.local_branches()
view.local_bookmarks()
.flat_map(|(_, target)| target.added_ids()),
)
.any(|id| id == commit_id)
@ -1411,7 +1412,7 @@ impl MutableRepo {
&& self.view().heads().contains(wc_commit.id())
{
// Abandon the working-copy commit we're leaving if it's
// discardable, not pointed by local branch or other working
// discardable, not pointed by local bookmark or other working
// copies, and a head commit.
self.record_abandoned_commit(wc_commit_id);
}
@ -1504,15 +1505,15 @@ impl MutableRepo {
self.view.mark_dirty();
}
pub fn get_local_branch(&self, name: &str) -> RefTarget {
self.view.with_ref(|v| v.get_local_branch(name).clone())
pub fn get_local_bookmark(&self, name: &str) -> RefTarget {
self.view.with_ref(|v| v.get_local_bookmark(name).clone())
}
pub fn set_local_branch_target(&mut self, name: &str, target: RefTarget) {
self.view_mut().set_local_branch_target(name, target);
pub fn set_local_bookmark_target(&mut self, name: &str, target: RefTarget) {
self.view_mut().set_local_bookmark_target(name, target);
}
pub fn merge_local_branch(
pub fn merge_local_bookmark(
&mut self,
name: &str,
base_target: &RefTarget,
@ -1520,22 +1521,22 @@ impl MutableRepo {
) {
let view = self.view.get_mut();
let index = self.index.as_index();
let self_target = view.get_local_branch(name);
let self_target = view.get_local_bookmark(name);
let new_target = merge_ref_targets(index, self_target, base_target, other_target);
view.set_local_branch_target(name, new_target);
view.set_local_bookmark_target(name, new_target);
}
pub fn get_remote_branch(&self, name: &str, remote_name: &str) -> RemoteRef {
pub fn get_remote_bookmark(&self, name: &str, remote_name: &str) -> RemoteRef {
self.view
.with_ref(|v| v.get_remote_branch(name, remote_name).clone())
.with_ref(|v| v.get_remote_bookmark(name, remote_name).clone())
}
pub fn set_remote_branch(&mut self, name: &str, remote_name: &str, remote_ref: RemoteRef) {
pub fn set_remote_bookmark(&mut self, name: &str, remote_name: &str, remote_ref: RemoteRef) {
self.view_mut()
.set_remote_branch(name, remote_name, remote_ref);
.set_remote_bookmark(name, remote_name, remote_ref);
}
fn merge_remote_branch(
fn merge_remote_bookmark(
&mut self,
name: &str,
remote_name: &str,
@ -1544,26 +1545,26 @@ impl MutableRepo {
) {
let view = self.view.get_mut();
let index = self.index.as_index();
let self_ref = view.get_remote_branch(name, remote_name);
let self_ref = view.get_remote_bookmark(name, remote_name);
let new_ref = merge_remote_refs(index, self_ref, base_ref, other_ref);
view.set_remote_branch(name, remote_name, new_ref);
view.set_remote_bookmark(name, remote_name, new_ref);
}
/// Merges the specified remote branch in to local branch, and starts
/// Merges the specified remote bookmark in to local bookmark, and starts
/// tracking it.
pub fn track_remote_branch(&mut self, name: &str, remote_name: &str) {
let mut remote_ref = self.get_remote_branch(name, remote_name);
pub fn track_remote_bookmark(&mut self, name: &str, remote_name: &str) {
let mut remote_ref = self.get_remote_bookmark(name, remote_name);
let base_target = remote_ref.tracking_target();
self.merge_local_branch(name, base_target, &remote_ref.target);
self.merge_local_bookmark(name, base_target, &remote_ref.target);
remote_ref.state = RemoteRefState::Tracking;
self.set_remote_branch(name, remote_name, remote_ref);
self.set_remote_bookmark(name, remote_name, remote_ref);
}
/// Stops tracking the specified remote branch.
pub fn untrack_remote_branch(&mut self, name: &str, remote_name: &str) {
let mut remote_ref = self.get_remote_branch(name, remote_name);
/// Stops tracking the specified remote bookmark.
pub fn untrack_remote_bookmark(&mut self, name: &str, remote_name: &str) {
let mut remote_ref = self.get_remote_bookmark(name, remote_name);
remote_ref.state = RemoteRefState::New;
self.set_remote_branch(name, remote_name, remote_ref);
self.set_remote_bookmark(name, remote_name, remote_ref);
}
pub fn remove_remote(&mut self, remote_name: &str) {
@ -1688,10 +1689,10 @@ impl MutableRepo {
self.view_mut().add_head(added_head);
}
let changed_local_branches =
diff_named_ref_targets(base.local_branches(), other.local_branches());
for (name, (base_target, other_target)) in changed_local_branches {
self.merge_local_branch(name, base_target, other_target);
let changed_local_bookmarks =
diff_named_ref_targets(base.local_bookmarks(), other.local_bookmarks());
for (name, (base_target, other_target)) in changed_local_bookmarks {
self.merge_local_bookmark(name, base_target, other_target);
}
let changed_tags = diff_named_ref_targets(base.tags(), other.tags());
@ -1704,10 +1705,10 @@ impl MutableRepo {
self.merge_git_ref(name, base_target, other_target);
}
let changed_remote_branches =
diff_named_remote_refs(base.all_remote_branches(), other.all_remote_branches());
for ((name, remote_name), (base_ref, other_ref)) in changed_remote_branches {
self.merge_remote_branch(name, remote_name, base_ref, other_ref);
let changed_remote_bookmarks =
diff_named_remote_refs(base.all_remote_bookmarks(), other.all_remote_bookmarks());
for ((name, remote_name), (base_ref, other_ref)) in changed_remote_bookmarks {
self.merge_remote_bookmark(name, remote_name, base_ref, other_ref);
}
let new_git_head_target = merge_ref_targets(

View file

@ -119,9 +119,9 @@ pub enum RevsetCommitRef {
},
VisibleHeads,
Root,
Branches(StringPattern),
RemoteBranches {
branch_pattern: StringPattern,
Bookmarks(StringPattern),
RemoteBookmarks {
bookmark_pattern: StringPattern,
remote_pattern: StringPattern,
remote_ref_state: Option<RemoteRefState>,
},
@ -256,20 +256,20 @@ impl RevsetExpression {
Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Root))
}
pub fn branches(pattern: StringPattern) -> Rc<RevsetExpression> {
Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Branches(
pub fn bookmarks(pattern: StringPattern) -> Rc<RevsetExpression> {
Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Bookmarks(
pattern,
)))
}
pub fn remote_branches(
branch_pattern: StringPattern,
pub fn remote_bookmarks(
bookmark_pattern: StringPattern,
remote_pattern: StringPattern,
remote_ref_state: Option<RemoteRefState>,
) -> Rc<RevsetExpression> {
Rc::new(RevsetExpression::CommitRef(
RevsetCommitRef::RemoteBranches {
branch_pattern,
RevsetCommitRef::RemoteBookmarks {
bookmark_pattern,
remote_pattern,
remote_ref_state,
},
@ -460,7 +460,7 @@ impl RevsetExpression {
}
/// Resolve a programmatically created revset expression. In particular, the
/// expression must not contain any symbols (branches, tags, change/commit
/// expression must not contain any symbols (bookmarks, tags, change/commit
/// prefixes). Callers must not include `RevsetExpression::symbol()` in
/// the expression, and should instead resolve symbols to `CommitId`s and
/// pass them into `RevsetExpression::commits()`. Similarly, the expression
@ -644,24 +644,34 @@ static BUILTIN_FUNCTION_MAP: Lazy<HashMap<&'static str, RevsetFunction>> = Lazy:
function.expect_no_arguments()?;
Ok(RevsetExpression::root())
});
map.insert("branches", |function, _context| {
map.insert("bookmarks", |function, _context| {
let ([], [opt_arg]) = function.expect_arguments()?;
let pattern = if let Some(arg) = opt_arg {
expect_string_pattern(arg)?
} else {
StringPattern::everything()
};
Ok(RevsetExpression::branches(pattern))
Ok(RevsetExpression::bookmarks(pattern))
});
map.insert("remote_branches", |function, _context| {
parse_remote_branches_arguments(function, None)
map.insert("remote_bookmarks", |function, _context| {
parse_remote_bookmarks_arguments(function, None)
});
map.insert("tracked_remote_branches", |function, _context| {
parse_remote_branches_arguments(function, Some(RemoteRefState::Tracking))
map.insert("tracked_remote_bookmarks", |function, _context| {
parse_remote_bookmarks_arguments(function, Some(RemoteRefState::Tracking))
});
map.insert("untracked_remote_branches", |function, _context| {
parse_remote_branches_arguments(function, Some(RemoteRefState::New))
map.insert("untracked_remote_bookmarks", |function, _context| {
parse_remote_bookmarks_arguments(function, Some(RemoteRefState::New))
});
// TODO: Remove in jj 0.28+
map.insert("branches", map["bookmarks"]);
map.insert("remote_branches", map["remote_bookmarks"]);
map.insert("tracked_remote_branches", map["tracked_remote_bookmarks"]);
map.insert(
"untracked_remote_branches",
map["untracked_remote_bookmarks"],
);
map.insert("tags", |function, _context| {
function.expect_no_arguments()?;
Ok(RevsetExpression::tags())
@ -822,14 +832,14 @@ pub fn expect_date_pattern(
revset_parser::expect_pattern_with("date pattern", node, parse_pattern)
}
fn parse_remote_branches_arguments(
fn parse_remote_bookmarks_arguments(
function: &FunctionCallNode,
remote_ref_state: Option<RemoteRefState>,
) -> Result<Rc<RevsetExpression>, RevsetParseError> {
let ([], [branch_opt_arg, remote_opt_arg]) =
let ([], [bookmark_opt_arg, remote_opt_arg]) =
function.expect_named_arguments(&["", "remote"])?;
let branch_pattern = if let Some(branch_arg) = branch_opt_arg {
expect_string_pattern(branch_arg)?
let bookmark_pattern = if let Some(bookmark_arg) = bookmark_opt_arg {
expect_string_pattern(bookmark_arg)?
} else {
StringPattern::everything()
};
@ -838,8 +848,8 @@ fn parse_remote_branches_arguments(
} else {
StringPattern::everything()
};
Ok(RevsetExpression::remote_branches(
branch_pattern,
Ok(RevsetExpression::remote_bookmarks(
bookmark_pattern,
remote_pattern,
remote_ref_state,
))
@ -1411,29 +1421,29 @@ pub fn walk_revs<'index>(
.evaluate_programmatic(repo)
}
fn resolve_remote_branch(repo: &dyn Repo, name: &str, remote: &str) -> Option<Vec<CommitId>> {
fn resolve_remote_bookmark(repo: &dyn Repo, name: &str, remote: &str) -> Option<Vec<CommitId>> {
let view = repo.view();
let target = match (name, remote) {
#[cfg(feature = "git")]
("HEAD", crate::git::REMOTE_NAME_FOR_LOCAL_GIT_REPO) => view.git_head(),
(name, remote) => &view.get_remote_branch(name, remote).target,
(name, remote) => &view.get_remote_bookmark(name, remote).target,
};
target
.is_present()
.then(|| target.added_ids().cloned().collect())
}
fn all_branch_symbols(
fn all_bookmark_symbols(
repo: &dyn Repo,
include_synced_remotes: bool,
) -> impl Iterator<Item = String> + '_ {
let view = repo.view();
view.branches()
.flat_map(move |(name, branch_target)| {
// Remote branch "x"@"y" may conflict with local "x@y" in unquoted form.
let local_target = branch_target.local_target;
view.bookmarks()
.flat_map(move |(name, bookmark_target)| {
// Remote bookmark "x"@"y" may conflict with local "x@y" in unquoted form.
let local_target = bookmark_target.local_target;
let local_symbol = local_target.is_present().then(|| name.to_owned());
let remote_symbols = branch_target
let remote_symbols = bookmark_target
.remote_refs
.into_iter()
.filter(move |&(_, remote_ref)| {
@ -1450,8 +1460,8 @@ fn all_branch_symbols(
fn make_no_such_symbol_error(repo: &dyn Repo, name: impl Into<String>) -> RevsetResolutionError {
let name = name.into();
// TODO: include tags?
let branch_names = all_branch_symbols(repo, name.contains('@'));
let candidates = collect_similar(&name, branch_names);
let bookmark_names = all_bookmark_symbols(repo, name.contains('@'));
let candidates = collect_similar(&name, bookmark_names);
RevsetResolutionError::NoSuchRevision { name, candidates }
}
@ -1508,7 +1518,7 @@ impl PartialSymbolResolver for BranchResolver {
repo: &dyn Repo,
symbol: &str,
) -> Result<Option<Vec<CommitId>>, RevsetResolutionError> {
let target = repo.view().get_local_branch(symbol);
let target = repo.view().get_local_bookmark(symbol);
Ok(target
.is_present()
.then(|| target.added_ids().cloned().collect()))
@ -1603,7 +1613,7 @@ impl PartialSymbolResolver for ChangePrefixResolver<'_> {
/// Each PartialSymbolResolver will be invoked in order, its result used if one
/// is provided. Native resolvers are always invoked first. In the future, we
/// may provide a way for extensions to override native resolvers like tags and
/// branches.
/// bookmarks.
pub trait SymbolResolverExtension {
/// PartialSymbolResolvers can capture `repo` for caching purposes if
/// desired, but they do not have to since `repo` is passed into
@ -1611,8 +1621,8 @@ pub trait SymbolResolverExtension {
fn new_resolvers<'a>(&self, repo: &'a dyn Repo) -> Vec<Box<dyn PartialSymbolResolver + 'a>>;
}
/// Resolves branches, remote branches, tags, git refs, and full and abbreviated
/// commit and change ids.
/// Resolves bookmarks, remote bookmarks, tags, git refs, and full and
/// abbreviated commit and change ids.
pub struct DefaultSymbolResolver<'a> {
repo: &'a dyn Repo,
commit_id_resolver: CommitPrefixResolver<'a>,
@ -1673,8 +1683,10 @@ fn resolve_commit_ref(
) -> Result<Vec<CommitId>, RevsetResolutionError> {
match commit_ref {
RevsetCommitRef::Symbol(symbol) => symbol_resolver.resolve_symbol(symbol),
RevsetCommitRef::RemoteSymbol { name, remote } => resolve_remote_branch(repo, name, remote)
.ok_or_else(|| make_no_such_symbol_error(repo, format!("{name}@{remote}"))),
RevsetCommitRef::RemoteSymbol { name, remote } => {
resolve_remote_bookmark(repo, name, remote)
.ok_or_else(|| make_no_such_symbol_error(repo, format!("{name}@{remote}")))
}
RevsetCommitRef::WorkingCopy(workspace_id) => {
if let Some(commit_id) = repo.view().get_wc_commit_id(workspace_id) {
Ok(vec![commit_id.clone()])
@ -1690,24 +1702,24 @@ fn resolve_commit_ref(
}
RevsetCommitRef::VisibleHeads => Ok(repo.view().heads().iter().cloned().collect_vec()),
RevsetCommitRef::Root => Ok(vec![repo.store().root_commit_id().clone()]),
RevsetCommitRef::Branches(pattern) => {
RevsetCommitRef::Bookmarks(pattern) => {
let commit_ids = repo
.view()
.local_branches_matching(pattern)
.local_bookmarks_matching(pattern)
.flat_map(|(_, target)| target.added_ids())
.cloned()
.collect();
Ok(commit_ids)
}
RevsetCommitRef::RemoteBranches {
branch_pattern,
RevsetCommitRef::RemoteBookmarks {
bookmark_pattern,
remote_pattern,
remote_ref_state,
} => {
// TODO: should we allow to select @git branches explicitly?
// TODO: should we allow to select @git bookmarks explicitly?
let commit_ids = repo
.view()
.remote_branches_matching(branch_pattern, remote_pattern)
.remote_bookmarks_matching(bookmark_pattern, remote_pattern)
.filter(|(_, remote_ref)| {
remote_ref_state.map_or(true, |state| remote_ref.state == state)
})
@ -1903,7 +1915,7 @@ impl VisibilityResolutionContext<'_> {
// transformation rules may subtly change the evaluated set. For example,
// `all() & x` is not `x` if `x` is hidden. This wouldn't matter in practice,
// but if it does, the heads set could be extended to include the commits
// (and `remote_branches()`) specified in the revset expression. Alternatively,
// (and `remote_bookmarks()`) specified in the revset expression. Alternatively,
// some optimization rules could be removed, but that means `author(_) & x`
// would have to test `::visible_heads() & x`.
ResolvedExpression::Ancestors {
@ -2411,32 +2423,32 @@ mod tests {
insta::assert_debug_snapshot!(
parse("foo").unwrap(),
@r###"CommitRef(Symbol("foo"))"###);
// Default arguments for *branches() are all ""
// Default arguments for *bookmarks() are all ""
insta::assert_debug_snapshot!(
parse("branches()").unwrap(),
@r###"CommitRef(Branches(Substring("")))"###);
insta::assert_debug_snapshot!(parse("remote_branches()").unwrap(), @r###"
parse("bookmarks()").unwrap(),
@r###"CommitRef(Bookmarks(Substring("")))"###);
insta::assert_debug_snapshot!(parse("remote_bookmarks()").unwrap(), @r###"
CommitRef(
RemoteBranches {
branch_pattern: Substring(""),
RemoteBookmarks {
bookmark_pattern: Substring(""),
remote_pattern: Substring(""),
remote_ref_state: None,
},
)
"###);
insta::assert_debug_snapshot!(parse("tracked_remote_branches()").unwrap(), @r###"
insta::assert_debug_snapshot!(parse("tracked_remote_bookmarks()").unwrap(), @r###"
CommitRef(
RemoteBranches {
branch_pattern: Substring(""),
RemoteBookmarks {
bookmark_pattern: Substring(""),
remote_pattern: Substring(""),
remote_ref_state: Some(Tracking),
},
)
"###);
insta::assert_debug_snapshot!(parse("untracked_remote_branches()").unwrap(), @r###"
insta::assert_debug_snapshot!(parse("untracked_remote_bookmarks()").unwrap(), @r###"
CommitRef(
RemoteBranches {
branch_pattern: Substring(""),
RemoteBookmarks {
bookmark_pattern: Substring(""),
remote_pattern: Substring(""),
remote_ref_state: Some(New),
},
@ -2565,22 +2577,22 @@ mod tests {
let _guard = settings.bind_to_scope();
insta::assert_debug_snapshot!(
parse(r#"branches("foo")"#).unwrap(),
@r###"CommitRef(Branches(Substring("foo")))"###);
parse(r#"bookmarks("foo")"#).unwrap(),
@r###"CommitRef(Bookmarks(Substring("foo")))"###);
insta::assert_debug_snapshot!(
parse(r#"branches(exact:"foo")"#).unwrap(),
@r###"CommitRef(Branches(Exact("foo")))"###);
parse(r#"bookmarks(exact:"foo")"#).unwrap(),
@r###"CommitRef(Bookmarks(Exact("foo")))"###);
insta::assert_debug_snapshot!(
parse(r#"branches(substring:"foo")"#).unwrap(),
@r###"CommitRef(Branches(Substring("foo")))"###);
parse(r#"bookmarks(substring:"foo")"#).unwrap(),
@r###"CommitRef(Bookmarks(Substring("foo")))"###);
insta::assert_debug_snapshot!(
parse(r#"branches(bad:"foo")"#).unwrap_err().kind(),
parse(r#"bookmarks(bad:"foo")"#).unwrap_err().kind(),
@r###"Expression("Invalid string pattern")"###);
insta::assert_debug_snapshot!(
parse(r#"branches(exact::"foo")"#).unwrap_err().kind(),
parse(r#"bookmarks(exact::"foo")"#).unwrap_err().kind(),
@r###"Expression("Expected expression of string pattern")"###);
insta::assert_debug_snapshot!(
parse(r#"branches(exact:"foo"+)"#).unwrap_err().kind(),
parse(r#"bookmarks(exact:"foo"+)"#).unwrap_err().kind(),
@r###"Expression("Expected expression of string pattern")"###);
// String pattern isn't allowed at top level.
@ -2698,74 +2710,74 @@ mod tests {
let _guard = settings.bind_to_scope();
insta::assert_debug_snapshot!(
parse("remote_branches(remote=foo)").unwrap(), @r###"
parse("remote_bookmarks(remote=foo)").unwrap(), @r###"
CommitRef(
RemoteBranches {
branch_pattern: Substring(""),
RemoteBookmarks {
bookmark_pattern: Substring(""),
remote_pattern: Substring("foo"),
remote_ref_state: None,
},
)
"###);
insta::assert_debug_snapshot!(
parse("remote_branches(foo, remote=bar)").unwrap(), @r###"
parse("remote_bookmarks(foo, remote=bar)").unwrap(), @r###"
CommitRef(
RemoteBranches {
branch_pattern: Substring("foo"),
RemoteBookmarks {
bookmark_pattern: Substring("foo"),
remote_pattern: Substring("bar"),
remote_ref_state: None,
},
)
"###);
insta::assert_debug_snapshot!(
parse("tracked_remote_branches(foo, remote=bar)").unwrap(), @r###"
parse("tracked_remote_bookmarks(foo, remote=bar)").unwrap(), @r###"
CommitRef(
RemoteBranches {
branch_pattern: Substring("foo"),
RemoteBookmarks {
bookmark_pattern: Substring("foo"),
remote_pattern: Substring("bar"),
remote_ref_state: Some(Tracking),
},
)
"###);
insta::assert_debug_snapshot!(
parse("untracked_remote_branches(foo, remote=bar)").unwrap(), @r###"
parse("untracked_remote_bookmarks(foo, remote=bar)").unwrap(), @r###"
CommitRef(
RemoteBranches {
branch_pattern: Substring("foo"),
RemoteBookmarks {
bookmark_pattern: Substring("foo"),
remote_pattern: Substring("bar"),
remote_ref_state: Some(New),
},
)
"###);
insta::assert_debug_snapshot!(
parse(r#"remote_branches(remote=foo, bar)"#).unwrap_err().kind(),
parse(r#"remote_bookmarks(remote=foo, bar)"#).unwrap_err().kind(),
@r###"
InvalidFunctionArguments {
name: "remote_branches",
name: "remote_bookmarks",
message: "Positional argument follows keyword argument",
}
"###);
insta::assert_debug_snapshot!(
parse(r#"remote_branches("", foo, remote=bar)"#).unwrap_err().kind(),
parse(r#"remote_bookmarks("", foo, remote=bar)"#).unwrap_err().kind(),
@r###"
InvalidFunctionArguments {
name: "remote_branches",
name: "remote_bookmarks",
message: "Got multiple values for keyword \"remote\"",
}
"###);
insta::assert_debug_snapshot!(
parse(r#"remote_branches(remote=bar, remote=bar)"#).unwrap_err().kind(),
parse(r#"remote_bookmarks(remote=bar, remote=bar)"#).unwrap_err().kind(),
@r###"
InvalidFunctionArguments {
name: "remote_branches",
name: "remote_bookmarks",
message: "Got multiple values for keyword \"remote\"",
}
"###);
insta::assert_debug_snapshot!(
parse(r#"remote_branches(unknown=bar)"#).unwrap_err().kind(),
parse(r#"remote_bookmarks(unknown=bar)"#).unwrap_err().kind(),
@r###"
InvalidFunctionArguments {
name: "remote_branches",
name: "remote_bookmarks",
message: "Unexpected keyword argument \"unknown\"",
}
"###);
@ -2833,61 +2845,61 @@ mod tests {
// (e.g. Range -> DagRange) nor reorders arguments unintentionally.
insta::assert_debug_snapshot!(
optimize(parse("parents(branches() & all())").unwrap()), @r###"
optimize(parse("parents(bookmarks() & all())").unwrap()), @r###"
Ancestors {
heads: CommitRef(Branches(Substring(""))),
heads: CommitRef(Bookmarks(Substring(""))),
generation: 1..2,
}
"###);
insta::assert_debug_snapshot!(
optimize(parse("children(branches() & all())").unwrap()), @r###"
optimize(parse("children(bookmarks() & all())").unwrap()), @r###"
Descendants {
roots: CommitRef(Branches(Substring(""))),
roots: CommitRef(Bookmarks(Substring(""))),
generation: 1..2,
}
"###);
insta::assert_debug_snapshot!(
optimize(parse("ancestors(branches() & all())").unwrap()), @r###"
optimize(parse("ancestors(bookmarks() & all())").unwrap()), @r###"
Ancestors {
heads: CommitRef(Branches(Substring(""))),
heads: CommitRef(Bookmarks(Substring(""))),
generation: 0..18446744073709551615,
}
"###);
insta::assert_debug_snapshot!(
optimize(parse("descendants(branches() & all())").unwrap()), @r###"
optimize(parse("descendants(bookmarks() & all())").unwrap()), @r###"
Descendants {
roots: CommitRef(Branches(Substring(""))),
roots: CommitRef(Bookmarks(Substring(""))),
generation: 0..18446744073709551615,
}
"###);
insta::assert_debug_snapshot!(
optimize(parse("(branches() & all())..(all() & tags())").unwrap()), @r###"
optimize(parse("(bookmarks() & all())..(all() & tags())").unwrap()), @r###"
Range {
roots: CommitRef(Branches(Substring(""))),
roots: CommitRef(Bookmarks(Substring(""))),
heads: CommitRef(Tags),
generation: 0..18446744073709551615,
}
"###);
insta::assert_debug_snapshot!(
optimize(parse("(branches() & all())::(all() & tags())").unwrap()), @r###"
optimize(parse("(bookmarks() & all())::(all() & tags())").unwrap()), @r###"
DagRange {
roots: CommitRef(Branches(Substring(""))),
roots: CommitRef(Bookmarks(Substring(""))),
heads: CommitRef(Tags),
}
"###);
insta::assert_debug_snapshot!(
optimize(parse("heads(branches() & all())").unwrap()),
@r###"Heads(CommitRef(Branches(Substring(""))))"###);
optimize(parse("heads(bookmarks() & all())").unwrap()),
@r###"Heads(CommitRef(Bookmarks(Substring(""))))"###);
insta::assert_debug_snapshot!(
optimize(parse("roots(branches() & all())").unwrap()),
@r###"Roots(CommitRef(Branches(Substring(""))))"###);
optimize(parse("roots(bookmarks() & all())").unwrap()),
@r###"Roots(CommitRef(Bookmarks(Substring(""))))"###);
insta::assert_debug_snapshot!(
optimize(parse("latest(branches() & all(), 2)").unwrap()), @r###"
optimize(parse("latest(bookmarks() & all(), 2)").unwrap()), @r###"
Latest {
candidates: CommitRef(Branches(Substring(""))),
candidates: CommitRef(Bookmarks(Substring(""))),
count: 2,
}
"###);
@ -2902,30 +2914,30 @@ mod tests {
)
"###);
insta::assert_debug_snapshot!(
optimize(parse("present(branches() & all())").unwrap()),
@r###"Present(CommitRef(Branches(Substring(""))))"###);
optimize(parse("present(bookmarks() & all())").unwrap()),
@r###"Present(CommitRef(Bookmarks(Substring(""))))"###);
insta::assert_debug_snapshot!(
optimize(parse("~branches() & all()").unwrap()),
@r###"NotIn(CommitRef(Branches(Substring(""))))"###);
optimize(parse("~bookmarks() & all()").unwrap()),
@r###"NotIn(CommitRef(Bookmarks(Substring(""))))"###);
insta::assert_debug_snapshot!(
optimize(parse("(branches() & all()) | (all() & tags())").unwrap()), @r###"
optimize(parse("(bookmarks() & all()) | (all() & tags())").unwrap()), @r###"
Union(
CommitRef(Branches(Substring(""))),
CommitRef(Bookmarks(Substring(""))),
CommitRef(Tags),
)
"###);
insta::assert_debug_snapshot!(
optimize(parse("(branches() & all()) & (all() & tags())").unwrap()), @r###"
optimize(parse("(bookmarks() & all()) & (all() & tags())").unwrap()), @r###"
Intersection(
CommitRef(Branches(Substring(""))),
CommitRef(Bookmarks(Substring(""))),
CommitRef(Tags),
)
"###);
insta::assert_debug_snapshot!(
optimize(parse("(branches() & all()) ~ (all() & tags())").unwrap()), @r###"
optimize(parse("(bookmarks() & all()) ~ (all() & tags())").unwrap()), @r###"
Difference(
CommitRef(Branches(Substring(""))),
CommitRef(Bookmarks(Substring(""))),
CommitRef(Tags),
)
"###);
@ -2947,20 +2959,20 @@ mod tests {
let optimized = optimize(parsed.clone());
assert!(Rc::ptr_eq(&parsed, &optimized));
let parsed = parse("branches() | tags()").unwrap();
let parsed = parse("bookmarks() | tags()").unwrap();
let optimized = optimize(parsed.clone());
assert!(Rc::ptr_eq(&parsed, &optimized));
let parsed = parse("branches() & tags()").unwrap();
let parsed = parse("bookmarks() & tags()").unwrap();
let optimized = optimize(parsed.clone());
assert!(Rc::ptr_eq(&parsed, &optimized));
// Only left subtree should be rewritten.
let parsed = parse("(branches() & all()) | tags()").unwrap();
let parsed = parse("(bookmarks() & all()) | tags()").unwrap();
let optimized = optimize(parsed.clone());
assert_matches!(
unwrap_union(&optimized).0.as_ref(),
RevsetExpression::CommitRef(RevsetCommitRef::Branches(_))
RevsetExpression::CommitRef(RevsetCommitRef::Bookmarks(_))
);
assert!(Rc::ptr_eq(
unwrap_union(&parsed).1,
@ -2968,7 +2980,7 @@ mod tests {
));
// Only right subtree should be rewritten.
let parsed = parse("branches() | (all() & tags())").unwrap();
let parsed = parse("bookmarks() | (all() & tags())").unwrap();
let optimized = optimize(parsed.clone());
assert!(Rc::ptr_eq(
unwrap_union(&parsed).0,

View file

@ -44,14 +44,14 @@ pub struct RepoSettings {
#[derive(Debug, Clone)]
pub struct GitSettings {
pub auto_local_branch: bool,
pub auto_local_bookmark: bool,
pub abandon_unreachable_commits: bool,
}
impl GitSettings {
pub fn from_config(config: &config::Config) -> Self {
GitSettings {
auto_local_branch: config.get_bool("git.auto-local-branch").unwrap_or(false),
auto_local_bookmark: config.get_bool("git.auto-local-branch").unwrap_or(false),
abandon_unreachable_commits: config
.get_bool("git.abandon-unreachable-commits")
.unwrap_or(true),
@ -62,7 +62,7 @@ impl GitSettings {
impl Default for GitSettings {
fn default() -> Self {
GitSettings {
auto_local_branch: false,
auto_local_bookmark: false,
abandon_unreachable_commits: true,
}
}
@ -190,9 +190,9 @@ impl UserSettings {
.unwrap_or_else(|_| whoami::username())
}
pub fn push_branch_prefix(&self) -> String {
pub fn push_bookmark_prefix(&self) -> String {
self.config
.get_string("git.push-branch-prefix")
.get_string("git.push-bookmark-prefix")
.unwrap_or_else(|_| "push-".to_string())
}

View file

@ -436,7 +436,7 @@ fn view_to_proto(view: &View) -> crate::protos::op_store::View {
proto.head_ids.push(head_id.to_bytes());
}
proto.branches = branch_views_to_proto_legacy(&view.local_branches, &view.remote_views);
proto.branches = bookmark_views_to_proto_legacy(&view.local_bookmarks, &view.remote_views);
for (name, target) in &view.tags {
proto.tags.push(crate::protos::op_store::Tag {
@ -475,8 +475,8 @@ fn view_from_proto(proto: crate::protos::op_store::View) -> View {
view.head_ids.insert(CommitId::new(head_id_bytes));
}
let (local_branches, remote_views) = branch_views_from_proto_legacy(proto.branches);
view.local_branches = local_branches;
let (local_bookmarks, remote_views) = bookmark_views_from_proto_legacy(proto.branches);
view.local_bookmarks = local_bookmarks;
view.remote_views = remote_views;
for tag_proto in proto.tags {
@ -508,14 +508,14 @@ fn view_from_proto(proto: crate::protos::op_store::View) -> View {
view
}
fn branch_views_to_proto_legacy(
local_branches: &BTreeMap<String, RefTarget>,
fn bookmark_views_to_proto_legacy(
local_bookmarks: &BTreeMap<String, RefTarget>,
remote_views: &BTreeMap<String, RemoteView>,
) -> Vec<crate::protos::op_store::Branch> {
op_store::merge_join_branch_views(local_branches, remote_views)
.map(|(name, branch_target)| {
let local_target = ref_target_to_proto(branch_target.local_target);
let remote_branches = branch_target
op_store::merge_join_bookmark_views(local_bookmarks, remote_views)
.map(|(name, bookmark_target)| {
let local_target = ref_target_to_proto(bookmark_target.local_target);
let remote_bookmarks = bookmark_target
.remote_refs
.iter()
.map(
@ -529,29 +529,30 @@ fn branch_views_to_proto_legacy(
crate::protos::op_store::Branch {
name: name.to_owned(),
local_target,
remote_branches,
remote_branches: remote_bookmarks,
}
})
.collect()
}
fn branch_views_from_proto_legacy(
branches_legacy: Vec<crate::protos::op_store::Branch>,
fn bookmark_views_from_proto_legacy(
bookmarks_legacy: Vec<crate::protos::op_store::Branch>,
) -> (BTreeMap<String, RefTarget>, BTreeMap<String, RemoteView>) {
let mut local_branches: BTreeMap<String, RefTarget> = BTreeMap::new();
let mut local_bookmarks: BTreeMap<String, RefTarget> = BTreeMap::new();
let mut remote_views: BTreeMap<String, RemoteView> = BTreeMap::new();
for branch_proto in branches_legacy {
let local_target = ref_target_from_proto(branch_proto.local_target);
for remote_branch in branch_proto.remote_branches {
let state = remote_ref_state_from_proto(remote_branch.state).unwrap_or_else(|| {
// If local branch doesn't exist, we assume that the remote branch hasn't been
// merged because git.auto-local-branch was off. That's probably more common
// than deleted but yet-to-be-pushed local branch. Alternatively, we could read
// git.auto-local-branch setting here, but that wouldn't always work since the
// setting could be toggled after the branch got merged.
for bookmark_proto in bookmarks_legacy {
let local_target = ref_target_from_proto(bookmark_proto.local_target);
for remote_bookmark in bookmark_proto.remote_branches {
let state = remote_ref_state_from_proto(remote_bookmark.state).unwrap_or_else(|| {
// If local bookmark doesn't exist, we assume that the remote bookmark hasn't
// been merged because git.auto-local-bookmark was off. That's
// probably more common than deleted but yet-to-be-pushed local
// bookmark. Alternatively, we could read
// git.auto-local-bookmark setting here, but that wouldn't always work since the
// setting could be toggled after the bookmark got merged.
#[cfg(feature = "git")]
let is_git_tracking =
remote_branch.remote_name == crate::git::REMOTE_NAME_FOR_LOCAL_GIT_REPO;
remote_bookmark.remote_name == crate::git::REMOTE_NAME_FOR_LOCAL_GIT_REPO;
#[cfg(not(feature = "git"))]
let is_git_tracking = false;
let default_state = if is_git_tracking || local_target.is_present() {
@ -560,27 +561,27 @@ fn branch_views_from_proto_legacy(
RemoteRefState::New
};
tracing::trace!(
?branch_proto.name,
?remote_branch.remote_name,
?bookmark_proto.name,
?remote_bookmark.remote_name,
?default_state,
"generated tracking state",
);
default_state
});
let remote_view = remote_views.entry(remote_branch.remote_name).or_default();
let remote_view = remote_views.entry(remote_bookmark.remote_name).or_default();
let remote_ref = RemoteRef {
target: ref_target_from_proto(remote_branch.target),
target: ref_target_from_proto(remote_bookmark.target),
state,
};
remote_view
.branches
.insert(branch_proto.name.clone(), remote_ref);
.bookmarks
.insert(bookmark_proto.name.clone(), remote_ref);
}
if local_target.is_present() {
local_branches.insert(branch_proto.name, local_target);
local_bookmarks.insert(bookmark_proto.name, local_target);
}
}
(local_branches, remote_views)
(local_bookmarks, remote_views)
}
fn migrate_git_refs_to_remote(view: &mut View) {
@ -589,17 +590,17 @@ fn migrate_git_refs_to_remote(view: &mut View) {
return;
}
tracing::info!("migrating Git-tracking branches");
tracing::info!("migrating Git-tracking bookmarks");
let mut git_view = RemoteView::default();
for (full_name, target) in &view.git_refs {
if let Some(name) = full_name.strip_prefix("refs/heads/") {
assert!(!name.is_empty());
let remote_ref = RemoteRef {
target: target.clone(),
// Git-tracking branches should never be untracked.
// Git-tracking bookmarks should never be untracked.
state: RemoteRefState::Tracking,
};
git_view.branches.insert(name.to_owned(), remote_ref);
git_view.bookmarks.insert(name.to_owned(), remote_ref);
}
}
#[cfg(feature = "git")]
@ -731,9 +732,9 @@ mod tests {
};
let head_id1 = CommitId::from_hex("aaa111");
let head_id2 = CommitId::from_hex("aaa222");
let branch_main_local_target = RefTarget::normal(CommitId::from_hex("ccc111"));
let branch_main_origin_target = RefTarget::normal(CommitId::from_hex("ccc222"));
let branch_deleted_origin_target = RefTarget::normal(CommitId::from_hex("ccc333"));
let bookmark_main_local_target = RefTarget::normal(CommitId::from_hex("ccc111"));
let bookmark_main_origin_target = RefTarget::normal(CommitId::from_hex("ccc222"));
let bookmark_deleted_origin_target = RefTarget::normal(CommitId::from_hex("ccc333"));
let tag_v1_target = RefTarget::normal(CommitId::from_hex("ddd111"));
let git_refs_main_target = RefTarget::normal(CommitId::from_hex("fff111"));
let git_refs_feature_target = RefTarget::from_legacy_form(
@ -744,17 +745,17 @@ mod tests {
let test_wc_commit_id = CommitId::from_hex("abc222");
View {
head_ids: hashset! {head_id1, head_id2},
local_branches: btreemap! {
"main".to_string() => branch_main_local_target,
local_bookmarks: btreemap! {
"main".to_string() => bookmark_main_local_target,
},
tags: btreemap! {
"v1.0".to_string() => tag_v1_target,
},
remote_views: btreemap! {
"origin".to_string() => RemoteView {
branches: btreemap! {
"main".to_string() => tracking_remote_ref(&branch_main_origin_target),
"deleted".to_string() => new_remote_ref(&branch_deleted_origin_target),
bookmarks: btreemap! {
"main".to_string() => tracking_remote_ref(&bookmark_main_origin_target),
"deleted".to_string() => new_remote_ref(&bookmark_deleted_origin_target),
},
},
},
@ -837,7 +838,7 @@ mod tests {
}
#[test]
fn test_branch_views_legacy_roundtrip() {
fn test_bookmark_views_legacy_roundtrip() {
let new_remote_ref = |target: &RefTarget| RemoteRef {
target: target.clone(),
state: RemoteRefState::New,
@ -846,49 +847,49 @@ mod tests {
target: target.clone(),
state: RemoteRefState::Tracking,
};
let local_branch1_target = RefTarget::normal(CommitId::from_hex("111111"));
let local_branch3_target = RefTarget::normal(CommitId::from_hex("222222"));
let git_branch1_target = RefTarget::normal(CommitId::from_hex("333333"));
let remote1_branch1_target = RefTarget::normal(CommitId::from_hex("444444"));
let remote2_branch2_target = RefTarget::normal(CommitId::from_hex("555555"));
let remote2_branch4_target = RefTarget::normal(CommitId::from_hex("666666"));
let local_branches = btreemap! {
"branch1".to_owned() => local_branch1_target.clone(),
"branch3".to_owned() => local_branch3_target.clone(),
let local_bookmark1_target = RefTarget::normal(CommitId::from_hex("111111"));
let local_bookmark3_target = RefTarget::normal(CommitId::from_hex("222222"));
let git_bookmark1_target = RefTarget::normal(CommitId::from_hex("333333"));
let remote1_bookmark1_target = RefTarget::normal(CommitId::from_hex("444444"));
let remote2_bookmark2_target = RefTarget::normal(CommitId::from_hex("555555"));
let remote2_bookmark4_target = RefTarget::normal(CommitId::from_hex("666666"));
let local_bookmarks = btreemap! {
"bookmark1".to_owned() => local_bookmark1_target.clone(),
"bookmark3".to_owned() => local_bookmark3_target.clone(),
};
let remote_views = btreemap! {
"git".to_owned() => RemoteView {
branches: btreemap! {
"branch1".to_owned() => tracking_remote_ref(&git_branch1_target),
bookmarks: btreemap! {
"bookmark1".to_owned() => tracking_remote_ref(&git_bookmark1_target),
},
},
"remote1".to_owned() => RemoteView {
branches: btreemap! {
"branch1".to_owned() => tracking_remote_ref(&remote1_branch1_target),
bookmarks: btreemap! {
"bookmark1".to_owned() => tracking_remote_ref(&remote1_bookmark1_target),
},
},
"remote2".to_owned() => RemoteView {
branches: btreemap! {
// "branch2" is non-tracking. "branch4" is tracking, but locally deleted.
"branch2".to_owned() => new_remote_ref(&remote2_branch2_target),
"branch4".to_owned() => tracking_remote_ref(&remote2_branch4_target),
bookmarks: btreemap! {
// "bookmark2" is non-tracking. "bookmark4" is tracking, but locally deleted.
"bookmark2".to_owned() => new_remote_ref(&remote2_bookmark2_target),
"bookmark4".to_owned() => tracking_remote_ref(&remote2_bookmark4_target),
},
},
};
let branches_legacy = branch_views_to_proto_legacy(&local_branches, &remote_views);
let bookmarks_legacy = bookmark_views_to_proto_legacy(&local_bookmarks, &remote_views);
assert_eq!(
branches_legacy
bookmarks_legacy
.iter()
.map(|proto| &proto.name)
.sorted()
.collect_vec(),
vec!["branch1", "branch2", "branch3", "branch4"],
vec!["bookmark1", "bookmark2", "bookmark3", "bookmark4"],
);
let (local_branches_reconstructed, remote_views_reconstructed) =
branch_views_from_proto_legacy(branches_legacy);
assert_eq!(local_branches_reconstructed, local_branches);
let (local_bookmarks_reconstructed, remote_views_reconstructed) =
bookmark_views_from_proto_legacy(bookmarks_legacy);
assert_eq!(local_bookmarks_reconstructed, local_bookmarks);
assert_eq!(remote_views_reconstructed, remote_views);
}
@ -903,17 +904,17 @@ mod tests {
target: normal_ref_target(id_hex),
state: RemoteRefState::Tracking,
};
let branch_to_proto =
let bookmark_to_proto =
|name: &str, local_ref_target, remote_branches| crate::protos::op_store::Branch {
name: name.to_owned(),
local_target: ref_target_to_proto(local_ref_target),
remote_branches,
};
let remote_branch_to_proto =
let remote_bookmark_to_proto =
|remote_name: &str, ref_target| crate::protos::op_store::RemoteBranch {
remote_name: remote_name.to_owned(),
target: ref_target_to_proto(ref_target),
state: None, // to be generated based on local branch existence
state: None, // to be generated based on local bookmark existence
};
let git_ref_to_proto = |name: &str, ref_target| crate::protos::op_store::GitRef {
name: name.to_owned(),
@ -923,18 +924,21 @@ mod tests {
let proto = crate::protos::op_store::View {
branches: vec![
branch_to_proto(
bookmark_to_proto(
"main",
&normal_ref_target("111111"),
vec![
remote_branch_to_proto("git", &normal_ref_target("222222")),
remote_branch_to_proto("gita", &normal_ref_target("333333")),
remote_bookmark_to_proto("git", &normal_ref_target("222222")),
remote_bookmark_to_proto("gita", &normal_ref_target("333333")),
],
),
branch_to_proto(
bookmark_to_proto(
"untracked",
RefTarget::absent_ref(),
vec![remote_branch_to_proto("gita", &normal_ref_target("777777"))],
vec![remote_bookmark_to_proto(
"gita",
&normal_ref_target("777777"),
)],
),
],
git_refs: vec![
@ -949,7 +953,7 @@ mod tests {
let view = view_from_proto(proto);
assert_eq!(
view.local_branches,
view.local_bookmarks,
btreemap! {
"main".to_owned() => normal_ref_target("111111"),
},
@ -958,12 +962,12 @@ mod tests {
view.remote_views,
btreemap! {
"git".to_owned() => RemoteView {
branches: btreemap! {
bookmarks: btreemap! {
"main".to_owned() => normal_tracking_remote_ref("444444"), // refs/heads/main
},
},
"gita".to_owned() => RemoteView {
branches: btreemap! {
bookmarks: btreemap! {
"main".to_owned() => normal_tracking_remote_ref("333333"),
"untracked".to_owned() => normal_new_remote_ref("777777"),
},
@ -979,7 +983,7 @@ mod tests {
},
);
// Once migrated, "git" remote branches shouldn't be populated again.
// Once migrated, "git" remote bookmarks shouldn't be populated again.
let mut proto = view_to_proto(&view);
assert!(proto.has_git_refs_migrated_to_remote);
proto.branches.clear();

View file

@ -70,9 +70,9 @@ impl View {
&self.data.head_ids
}
/// Iterates pair of local and remote branches by branch name.
pub fn branches(&self) -> impl Iterator<Item = (&str, BranchTarget<'_>)> {
op_store::merge_join_branch_views(&self.data.local_branches, &self.data.remote_views)
/// Iterates pair of local and remote bookmarks by bookmark name.
pub fn bookmarks(&self) -> impl Iterator<Item = (&str, BranchTarget<'_>)> {
op_store::merge_join_bookmark_views(&self.data.local_bookmarks, &self.data.remote_views)
}
pub fn tags(&self) -> &BTreeMap<String, RefTarget> {
@ -103,63 +103,64 @@ impl View {
self.data.head_ids.remove(head_id);
}
/// Iterates local branch `(name, target)`s in lexicographical order.
pub fn local_branches(&self) -> impl Iterator<Item = (&str, &RefTarget)> {
/// Iterates local bookmark `(name, target)`s in lexicographical order.
pub fn local_bookmarks(&self) -> impl Iterator<Item = (&str, &RefTarget)> {
self.data
.local_branches
.local_bookmarks
.iter()
.map(|(name, target)| (name.as_ref(), target))
}
/// Iterates local branches `(name, target)` in lexicographical order where
/// Iterates local bookmarks `(name, target)` in lexicographical order where
/// the target adds `commit_id`.
pub fn local_branches_for_commit<'a: 'b, 'b>(
pub fn local_bookmarks_for_commit<'a: 'b, 'b>(
&'a self,
commit_id: &'b CommitId,
) -> impl Iterator<Item = (&'a str, &'a RefTarget)> + 'b {
self.local_branches()
self.local_bookmarks()
.filter(|(_, target)| target.added_ids().contains(commit_id))
}
/// Iterates local branch `(name, target)`s matching the given pattern.
/// Iterates local bookmark `(name, target)`s matching the given pattern.
/// Entries are sorted by `name`.
pub fn local_branches_matching<'a: 'b, 'b>(
pub fn local_bookmarks_matching<'a: 'b, 'b>(
&'a self,
pattern: &'b StringPattern,
) -> impl Iterator<Item = (&'a str, &'a RefTarget)> + 'b {
pattern
.filter_btree_map(&self.data.local_branches)
.filter_btree_map(&self.data.local_bookmarks)
.map(|(name, target)| (name.as_ref(), target))
}
pub fn get_local_branch(&self, name: &str) -> &RefTarget {
self.data.local_branches.get(name).flatten()
pub fn get_local_bookmark(&self, name: &str) -> &RefTarget {
self.data.local_bookmarks.get(name).flatten()
}
/// Sets local branch to point to the given target. If the target is absent,
/// and if no associated remote branches exist, the branch will be removed.
pub fn set_local_branch_target(&mut self, name: &str, target: RefTarget) {
/// Sets local bookmark to point to the given target. If the target is
/// absent, and if no associated remote bookmarks exist, the bookmark
/// will be removed.
pub fn set_local_bookmark_target(&mut self, name: &str, target: RefTarget) {
if target.is_present() {
self.data.local_branches.insert(name.to_owned(), target);
self.data.local_bookmarks.insert(name.to_owned(), target);
} else {
self.data.local_branches.remove(name);
self.data.local_bookmarks.remove(name);
}
}
/// Iterates over `((name, remote_name), remote_ref)` for all remote
/// branches in lexicographical order.
pub fn all_remote_branches(&self) -> impl Iterator<Item = ((&str, &str), &RemoteRef)> {
op_store::flatten_remote_branches(&self.data.remote_views)
/// bookmarks in lexicographical order.
pub fn all_remote_bookmarks(&self) -> impl Iterator<Item = ((&str, &str), &RemoteRef)> {
op_store::flatten_remote_bookmarks(&self.data.remote_views)
}
/// Iterates over `(name, remote_ref)`s for all remote branches of the
/// Iterates over `(name, remote_ref)`s for all remote bookmarks of the
/// specified remote in lexicographical order.
pub fn remote_branches(&self, remote_name: &str) -> impl Iterator<Item = (&str, &RemoteRef)> {
pub fn remote_bookmarks(&self, remote_name: &str) -> impl Iterator<Item = (&str, &RemoteRef)> {
let maybe_remote_view = self.data.remote_views.get(remote_name);
maybe_remote_view
.map(|remote_view| {
remote_view
.branches
.bookmarks
.iter()
.map(|(name, remote_ref)| (name.as_ref(), remote_ref))
})
@ -167,94 +168,97 @@ impl View {
.flatten()
}
/// Iterates over `(name, remote_ref)`s for all remote branches of the
/// Iterates over `(name, remote_ref)`s for all remote bookmarks of the
/// specified remote that match the given pattern.
///
/// Entries are sorted by `(name, remote_name)`.
pub fn remote_branches_matching<'a: 'b, 'b>(
pub fn remote_bookmarks_matching<'a: 'b, 'b>(
&'a self,
branch_pattern: &'b StringPattern,
bookmark_pattern: &'b StringPattern,
remote_pattern: &'b StringPattern,
) -> impl Iterator<Item = ((&'a str, &'a str), &'a RemoteRef)> + 'b {
// Use kmerge instead of flat_map for consistency with all_remote_branches().
// Use kmerge instead of flat_map for consistency with all_remote_bookmarks().
remote_pattern
.filter_btree_map(&self.data.remote_views)
.map(|(remote_name, remote_view)| {
branch_pattern.filter_btree_map(&remote_view.branches).map(
|(branch_name, remote_ref)| {
let full_name = (branch_name.as_ref(), remote_name.as_ref());
bookmark_pattern
.filter_btree_map(&remote_view.bookmarks)
.map(|(bookmark_name, remote_ref)| {
let full_name = (bookmark_name.as_ref(), remote_name.as_ref());
(full_name, remote_ref)
},
)
})
})
.kmerge_by(|(full_name1, _), (full_name2, _)| full_name1 < full_name2)
}
pub fn get_remote_branch(&self, name: &str, remote_name: &str) -> &RemoteRef {
pub fn get_remote_bookmark(&self, name: &str, remote_name: &str) -> &RemoteRef {
if let Some(remote_view) = self.data.remote_views.get(remote_name) {
remote_view.branches.get(name).flatten()
remote_view.bookmarks.get(name).flatten()
} else {
RemoteRef::absent_ref()
}
}
/// Sets remote-tracking branch to the given target and state. If the target
/// is absent, the branch will be removed.
pub fn set_remote_branch(&mut self, name: &str, remote_name: &str, remote_ref: RemoteRef) {
/// Sets remote-tracking bookmark to the given target and state. If the
/// target is absent, the bookmark will be removed.
pub fn set_remote_bookmark(&mut self, name: &str, remote_name: &str, remote_ref: RemoteRef) {
if remote_ref.is_present() {
let remote_view = self
.data
.remote_views
.entry(remote_name.to_owned())
.or_default();
remote_view.branches.insert(name.to_owned(), remote_ref);
remote_view.bookmarks.insert(name.to_owned(), remote_ref);
} else if let Some(remote_view) = self.data.remote_views.get_mut(remote_name) {
remote_view.branches.remove(name);
remote_view.bookmarks.remove(name);
}
}
/// Iterates over `(name, {local_ref, remote_ref})`s for every branch
/// Iterates over `(name, {local_ref, remote_ref})`s for every bookmark
/// present locally and/or on the specified remote, in lexicographical
/// order.
///
/// Note that this does *not* take into account whether the local branch
/// tracks the remote branch or not. Missing values are represented as
/// Note that this does *not* take into account whether the local bookmark
/// tracks the remote bookmark or not. Missing values are represented as
/// RefTarget::absent_ref() or RemoteRef::absent_ref().
pub fn local_remote_branches<'a>(
pub fn local_remote_bookmarks<'a>(
&'a self,
remote_name: &str,
) -> impl Iterator<Item = (&'a str, LocalAndRemoteRef<'a>)> + 'a {
refs::iter_named_local_remote_refs(self.local_branches(), self.remote_branches(remote_name))
.map(|(name, (local_target, remote_ref))| {
let targets = LocalAndRemoteRef {
local_target,
remote_ref,
};
(name, targets)
})
refs::iter_named_local_remote_refs(
self.local_bookmarks(),
self.remote_bookmarks(remote_name),
)
.map(|(name, (local_target, remote_ref))| {
let targets = LocalAndRemoteRef {
local_target,
remote_ref,
};
(name, targets)
})
}
/// Iterates over `(name, TrackingRefPair {local_ref, remote_ref})`s for
/// every branch with a name that matches the given pattern, and that is
/// every bookmark with a name that matches the given pattern, and that is
/// present locally and/or on the specified remote.
///
/// Entries are sorted by `name`.
///
/// Note that this does *not* take into account whether the local branch
/// tracks the remote branch or not. Missing values are represented as
/// Note that this does *not* take into account whether the local bookmark
/// tracks the remote bookmark or not. Missing values are represented as
/// RefTarget::absent_ref() or RemoteRef::absent_ref().
pub fn local_remote_branches_matching<'a: 'b, 'b>(
pub fn local_remote_bookmarks_matching<'a: 'b, 'b>(
&'a self,
branch_pattern: &'b StringPattern,
bookmark_pattern: &'b StringPattern,
remote_name: &str,
) -> impl Iterator<Item = (&'a str, LocalAndRemoteRef<'a>)> + 'b {
// Change remote_name to StringPattern if needed, but merge-join adapter won't
// be usable.
let maybe_remote_view = self.data.remote_views.get(remote_name);
refs::iter_named_local_remote_refs(
branch_pattern.filter_btree_map(&self.data.local_branches),
bookmark_pattern.filter_btree_map(&self.data.local_bookmarks),
maybe_remote_view
.map(|remote_view| branch_pattern.filter_btree_map(&remote_view.branches))
.map(|remote_view| bookmark_pattern.filter_btree_map(&remote_view.bookmarks))
.into_iter()
.flatten(),
)
@ -313,10 +317,10 @@ impl View {
/// Iterates all commit ids referenced by this view.
///
/// This can include hidden commits referenced by remote branches, previous
/// positions of conflicted branches, etc. The ancestors and predecessors of
/// the returned commits should be considered reachable from the view. Use
/// this to build commit index from scratch.
/// This can include hidden commits referenced by remote bookmarks, previous
/// positions of conflicted bookmarks, etc. The ancestors and predecessors
/// of the returned commits should be considered reachable from the
/// view. Use this to build commit index from scratch.
///
/// The iteration order is unspecified, and may include duplicated entries.
pub fn all_referenced_commit_ids(&self) -> impl Iterator<Item = &CommitId> {
@ -330,7 +334,7 @@ impl View {
// not be smart here. Callers will build a larger set of commits anyway.
let op_store::View {
head_ids,
local_branches,
local_bookmarks,
tags,
remote_views,
git_refs,
@ -339,11 +343,11 @@ impl View {
} = &self.data;
itertools::chain!(
head_ids,
local_branches.values().flat_map(ref_target_ids),
local_bookmarks.values().flat_map(ref_target_ids),
tags.values().flat_map(ref_target_ids),
remote_views.values().flat_map(|remote_view| {
let op_store::RemoteView { branches } = remote_view;
branches
let op_store::RemoteView { bookmarks } = remote_view;
bookmarks
.values()
.flat_map(|remote_ref| ref_target_ids(&remote_ref.target))
}),

File diff suppressed because it is too large Load diff

View file

@ -355,7 +355,7 @@ fn test_index_commits_hidden_but_referenced() {
tx.repo_mut().remove_head(commit_a.id());
tx.repo_mut().remove_head(commit_b.id());
tx.repo_mut().remove_head(commit_c.id());
tx.repo_mut().set_remote_branch(
tx.repo_mut().set_remote_bookmark(
"branch",
"origin",
RemoteRef {

View file

@ -191,9 +191,9 @@ fn test_edit_previous_empty_with_description() {
}
#[test]
fn test_edit_previous_empty_with_local_branch() {
fn test_edit_previous_empty_with_local_bookmark() {
// Test that MutableRepo::edit() does not abandon the previous commit if it
// is pointed by local branch.
// is pointed by local bookmark.
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
@ -208,7 +208,7 @@ fn test_edit_previous_empty_with_local_branch() {
)
.write()
.unwrap();
mut_repo.set_local_branch_target("b", RefTarget::normal(old_wc_commit.id().clone()));
mut_repo.set_local_bookmark_target("b", RefTarget::normal(old_wc_commit.id().clone()));
let ws_id = WorkspaceId::default();
mut_repo.edit(ws_id.clone(), &old_wc_commit).unwrap();
let repo = tx.commit("test");
@ -467,7 +467,7 @@ fn test_remove_head() {
#[test]
fn test_has_changed() {
// Test that MutableRepo::has_changed() reports changes iff the view has changed
// (e.g. not after setting a branch to point to where it was already
// (e.g. not after setting a bookmark to point to where it was already
// pointing).
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
@ -486,8 +486,8 @@ fn test_has_changed() {
mut_repo
.set_wc_commit(ws_id.clone(), commit1.id().clone())
.unwrap();
mut_repo.set_local_branch_target("main", RefTarget::normal(commit1.id().clone()));
mut_repo.set_remote_branch("main", "origin", normal_remote_ref(commit1.id()));
mut_repo.set_local_bookmark_target("main", RefTarget::normal(commit1.id().clone()));
mut_repo.set_remote_bookmark("main", "origin", normal_remote_ref(commit1.id()));
let repo = tx.commit("test");
// Test the setup
assert_eq!(repo.view().heads(), &hashset! {commit1.id().clone()});
@ -499,13 +499,13 @@ fn test_has_changed() {
mut_repo
.set_wc_commit(ws_id.clone(), commit1.id().clone())
.unwrap();
mut_repo.set_local_branch_target("main", RefTarget::normal(commit1.id().clone()));
mut_repo.set_remote_branch("main", "origin", normal_remote_ref(commit1.id()));
mut_repo.set_local_bookmark_target("main", RefTarget::normal(commit1.id().clone()));
mut_repo.set_remote_bookmark("main", "origin", normal_remote_ref(commit1.id()));
assert!(!mut_repo.has_changes());
mut_repo.remove_head(commit2.id());
mut_repo.set_local_branch_target("stable", RefTarget::absent());
mut_repo.set_remote_branch("stable", "origin", RemoteRef::absent());
mut_repo.set_local_bookmark_target("stable", RefTarget::absent());
mut_repo.set_remote_bookmark("stable", "origin", RemoteRef::absent());
assert!(!mut_repo.has_changes());
mut_repo.add_head(&commit2).unwrap();
@ -520,14 +520,14 @@ fn test_has_changed() {
mut_repo.set_wc_commit(ws_id, commit1.id().clone()).unwrap();
assert!(!mut_repo.has_changes());
mut_repo.set_local_branch_target("main", RefTarget::normal(commit2.id().clone()));
mut_repo.set_local_bookmark_target("main", RefTarget::normal(commit2.id().clone()));
assert!(mut_repo.has_changes());
mut_repo.set_local_branch_target("main", RefTarget::normal(commit1.id().clone()));
mut_repo.set_local_bookmark_target("main", RefTarget::normal(commit1.id().clone()));
assert!(!mut_repo.has_changes());
mut_repo.set_remote_branch("main", "origin", normal_remote_ref(commit2.id()));
mut_repo.set_remote_bookmark("main", "origin", normal_remote_ref(commit2.id()));
assert!(mut_repo.has_changes());
mut_repo.set_remote_branch("main", "origin", normal_remote_ref(commit1.id()));
mut_repo.set_remote_bookmark("main", "origin", normal_remote_ref(commit1.id()));
assert!(!mut_repo.has_changes());
}
@ -617,11 +617,11 @@ fn test_rename_remote() {
target: RefTarget::normal(commit.id().clone()),
state: RemoteRefState::Tracking, // doesn't matter
};
mut_repo.set_remote_branch("main", "origin", remote_ref.clone());
mut_repo.set_remote_bookmark("main", "origin", remote_ref.clone());
mut_repo.rename_remote("origin", "upstream");
assert_eq!(mut_repo.get_remote_branch("main", "upstream"), remote_ref);
assert_eq!(mut_repo.get_remote_bookmark("main", "upstream"), remote_ref);
assert_eq!(
mut_repo.get_remote_branch("main", "origin"),
mut_repo.get_remote_bookmark("main", "origin"),
RemoteRef::absent()
);
}

View file

@ -263,7 +263,7 @@ fn test_reparent_range_linear() {
}
#[test]
fn test_reparent_range_branchy() {
fn test_reparent_range_bookmarky() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo_0 = test_repo.repo;
@ -279,7 +279,7 @@ fn test_reparent_range_branchy() {
parents.try_into().unwrap()
}
// Set up branchy operation graph:
// Set up bookmarky operation graph:
// G
// |\
// | F

View file

@ -255,7 +255,7 @@ fn test_resolve_symbol_change_id(readonly: bool) {
for i in &[133, 664, 840, 5085] {
let git_commit_id = git_repo
.commit(
Some(&format!("refs/heads/branch{i}")),
Some(&format!("refs/heads/bookmark{i}")),
&git_author,
&git_committer,
&format!("test {i}"),
@ -445,7 +445,7 @@ fn test_resolve_working_copies() {
}
#[test]
fn test_resolve_symbol_branches() {
fn test_resolve_symbol_bookmarks() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
@ -469,42 +469,42 @@ fn test_resolve_symbol_branches() {
let commit4 = write_random_commit(mut_repo, &settings);
let commit5 = write_random_commit(mut_repo, &settings);
mut_repo.set_local_branch_target("local", RefTarget::normal(commit1.id().clone()));
mut_repo.set_remote_branch("remote", "origin", normal_tracking_remote_ref(commit2.id()));
mut_repo.set_local_branch_target("local-remote", RefTarget::normal(commit3.id().clone()));
mut_repo.set_remote_branch(
mut_repo.set_local_bookmark_target("local", RefTarget::normal(commit1.id().clone()));
mut_repo.set_remote_bookmark("remote", "origin", normal_tracking_remote_ref(commit2.id()));
mut_repo.set_local_bookmark_target("local-remote", RefTarget::normal(commit3.id().clone()));
mut_repo.set_remote_bookmark(
"local-remote",
"origin",
normal_tracking_remote_ref(commit4.id()),
);
mut_repo.set_local_branch_target(
"local-remote@origin", // not a remote branch
mut_repo.set_local_bookmark_target(
"local-remote@origin", // not a remote bookmark
RefTarget::normal(commit5.id().clone()),
);
mut_repo.set_remote_branch(
mut_repo.set_remote_bookmark(
"local-remote",
"mirror",
tracking_remote_ref(mut_repo.get_local_branch("local-remote")),
tracking_remote_ref(mut_repo.get_local_bookmark("local-remote")),
);
mut_repo.set_remote_branch(
mut_repo.set_remote_bookmark(
"local-remote",
"untracked",
new_remote_ref(mut_repo.get_local_branch("local-remote")),
new_remote_ref(mut_repo.get_local_bookmark("local-remote")),
);
mut_repo.set_remote_branch(
mut_repo.set_remote_bookmark(
"local-remote",
git::REMOTE_NAME_FOR_LOCAL_GIT_REPO,
tracking_remote_ref(mut_repo.get_local_branch("local-remote")),
tracking_remote_ref(mut_repo.get_local_bookmark("local-remote")),
);
mut_repo.set_local_branch_target(
mut_repo.set_local_bookmark_target(
"local-conflicted",
RefTarget::from_legacy_form(
[commit1.id().clone()],
[commit3.id().clone(), commit2.id().clone()],
),
);
mut_repo.set_remote_branch(
mut_repo.set_remote_bookmark(
"remote-conflicted",
"origin",
tracking_remote_ref(RefTarget::from_legacy_form(
@ -580,11 +580,11 @@ fn test_resolve_symbol_branches() {
vec![commit5.id().clone(), commit4.id().clone()],
);
// Typo of local/remote branch name:
// Typo of local/remote bookmark name:
// For "local-emote" (without @remote part), "local-remote@mirror"/"@git" aren't
// suggested since they point to the same target as "local-remote". OTOH,
// "local-remote@untracked" is suggested because non-tracking branch is
// unrelated to the local branch of the same name.
// "local-remote@untracked" is suggested because non-tracking bookmark is
// unrelated to the local bookmark of the same name.
insta::assert_debug_snapshot!(
resolve_symbol(mut_repo, "local-emote").unwrap_err(), @r###"
NoSuchRevision {
@ -643,7 +643,7 @@ fn test_resolve_symbol_branches() {
}
"###);
// Typo of remote-only branch name
// Typo of remote-only bookmark name
insta::assert_debug_snapshot!(
resolve_symbol(mut_repo, "emote").unwrap_err(), @r###"
NoSuchRevision {
@ -690,16 +690,16 @@ fn test_resolve_symbol_tags() {
let commit2 = write_random_commit(mut_repo, &settings);
let commit3 = write_random_commit(mut_repo, &settings);
mut_repo.set_tag_target("tag-branch", RefTarget::normal(commit1.id().clone()));
mut_repo.set_local_branch_target("tag-branch", RefTarget::normal(commit2.id().clone()));
mut_repo.set_tag_target("tag-bookmark", RefTarget::normal(commit1.id().clone()));
mut_repo.set_local_bookmark_target("tag-bookmark", RefTarget::normal(commit2.id().clone()));
mut_repo.set_git_ref_target(
"refs/tags/unimported",
RefTarget::normal(commit3.id().clone()),
);
// Tag precedes branch
// Tag precedes bookmark
assert_eq!(
resolve_symbol(mut_repo, "tag-branch").unwrap(),
resolve_symbol(mut_repo, "tag-bookmark").unwrap(),
vec![commit1.id().clone()],
);
@ -785,11 +785,11 @@ fn test_resolve_symbol_git_refs() {
let commit4 = write_random_commit(mut_repo, &settings);
let commit5 = write_random_commit(mut_repo, &settings);
mut_repo.set_git_ref_target(
"refs/heads/branch1",
"refs/heads/bookmark1",
RefTarget::normal(commit1.id().clone()),
);
mut_repo.set_git_ref_target(
"refs/heads/branch2",
"refs/heads/bookmark2",
RefTarget::normal(commit2.id().clone()),
);
mut_repo.set_git_ref_target(
@ -801,7 +801,7 @@ fn test_resolve_symbol_git_refs() {
);
mut_repo.set_git_ref_target("refs/tags/tag1", RefTarget::normal(commit2.id().clone()));
mut_repo.set_git_ref_target(
"refs/tags/remotes/origin/branch1",
"refs/tags/remotes/origin/bookmark1",
RefTarget::normal(commit3.id().clone()),
);
@ -813,26 +813,35 @@ fn test_resolve_symbol_git_refs() {
);
// Full ref
mut_repo.set_git_ref_target("refs/heads/branch", RefTarget::normal(commit4.id().clone()));
mut_repo.set_git_ref_target(
"refs/heads/bookmark",
RefTarget::normal(commit4.id().clone()),
);
assert_eq!(
resolve_symbol(mut_repo, "refs/heads/branch").unwrap(),
resolve_symbol(mut_repo, "refs/heads/bookmark").unwrap(),
vec![commit4.id().clone()]
);
// Qualified with only heads/
mut_repo.set_git_ref_target("refs/heads/branch", RefTarget::normal(commit5.id().clone()));
mut_repo.set_git_ref_target("refs/tags/branch", RefTarget::normal(commit4.id().clone()));
// branch alone is not recognized
mut_repo.set_git_ref_target(
"refs/heads/bookmark",
RefTarget::normal(commit5.id().clone()),
);
mut_repo.set_git_ref_target(
"refs/tags/bookmark",
RefTarget::normal(commit4.id().clone()),
);
// bookmark alone is not recognized
insta::assert_debug_snapshot!(
resolve_symbol(mut_repo, "branch").unwrap_err(), @r###"
resolve_symbol(mut_repo, "bookmark").unwrap_err(), @r###"
NoSuchRevision {
name: "branch",
name: "bookmark",
candidates: [],
}
"###);
// heads/branch does get resolved to the git ref refs/heads/branch
// heads/bookmark does get resolved to the git ref refs/heads/bookmark
assert_eq!(
resolve_symbol(mut_repo, "heads/branch").unwrap(),
resolve_symbol(mut_repo, "heads/bookmark").unwrap(),
vec![commit5.id().clone()]
);
@ -843,13 +852,13 @@ fn test_resolve_symbol_git_refs() {
Err(RevsetResolutionError::NoSuchRevision { .. })
);
// Unqualified remote-tracking branch name
// Unqualified remote-tracking bookmark name
mut_repo.set_git_ref_target(
"refs/remotes/origin/remote-branch",
"refs/remotes/origin/remote-bookmark",
RefTarget::normal(commit2.id().clone()),
);
assert_matches!(
resolve_symbol(mut_repo, "origin/remote-branch"),
resolve_symbol(mut_repo, "origin/remote-bookmark"),
Err(RevsetResolutionError::NoSuchRevision { .. })
);
@ -1941,7 +1950,7 @@ fn test_evaluate_expression_git_refs() {
assert_eq!(resolve_commit_ids(mut_repo, "git_refs()"), vec![]);
// Can get a mix of git refs
mut_repo.set_git_ref_target(
"refs/heads/branch1",
"refs/heads/bookmark1",
RefTarget::normal(commit1.id().clone()),
);
mut_repo.set_git_ref_target("refs/tags/tag1", RefTarget::normal(commit2.id().clone()));
@ -1958,7 +1967,7 @@ fn test_evaluate_expression_git_refs() {
);
// Can get git refs when there are conflicted refs
mut_repo.set_git_ref_target(
"refs/heads/branch1",
"refs/heads/bookmark1",
RefTarget::from_legacy_form(
[commit1.id().clone()],
[commit2.id().clone(), commit3.id().clone()],
@ -2003,7 +2012,7 @@ fn test_evaluate_expression_git_head() {
}
#[test]
fn test_evaluate_expression_branches() {
fn test_evaluate_expression_bookmarks() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
@ -2016,75 +2025,75 @@ fn test_evaluate_expression_branches() {
let commit3 = write_random_commit(mut_repo, &settings);
let commit4 = write_random_commit(mut_repo, &settings);
// Can get branches when there are none
assert_eq!(resolve_commit_ids(mut_repo, "branches()"), vec![]);
// Can get a few branches
mut_repo.set_local_branch_target("branch1", RefTarget::normal(commit1.id().clone()));
mut_repo.set_local_branch_target("branch2", RefTarget::normal(commit2.id().clone()));
// Can get bookmarks when there are none
assert_eq!(resolve_commit_ids(mut_repo, "bookmarks()"), vec![]);
// Can get a few bookmarks
mut_repo.set_local_bookmark_target("bookmark1", RefTarget::normal(commit1.id().clone()));
mut_repo.set_local_bookmark_target("bookmark2", RefTarget::normal(commit2.id().clone()));
assert_eq!(
resolve_commit_ids(mut_repo, "branches()"),
resolve_commit_ids(mut_repo, "bookmarks()"),
vec![commit2.id().clone(), commit1.id().clone()]
);
// Can get branches with matching names
// Can get bookmarks with matching names
assert_eq!(
resolve_commit_ids(mut_repo, "branches(branch1)"),
resolve_commit_ids(mut_repo, "bookmarks(bookmark1)"),
vec![commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "branches(branch)"),
resolve_commit_ids(mut_repo, "bookmarks(bookmark)"),
vec![commit2.id().clone(), commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "branches(exact:branch1)"),
resolve_commit_ids(mut_repo, "bookmarks(exact:bookmark1)"),
vec![commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, r#"branches(glob:"Branch?")"#),
resolve_commit_ids(mut_repo, r#"bookmarks(glob:"Bookmark?")"#),
vec![]
);
assert_eq!(
resolve_commit_ids(mut_repo, r#"branches(glob-i:"Branch?")"#),
resolve_commit_ids(mut_repo, r#"bookmarks(glob-i:"Bookmark?")"#),
vec![commit2.id().clone(), commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "branches(regex:'ranch')"),
resolve_commit_ids(mut_repo, "bookmarks(regex:'ookmark')"),
vec![commit2.id().clone(), commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "branches(regex:'^[Bb]ranch1$')"),
resolve_commit_ids(mut_repo, "bookmarks(regex:'^[Bb]ookmark1$')"),
vec![commit1.id().clone()]
);
// Can silently resolve to an empty set if there's no matches
assert_eq!(resolve_commit_ids(mut_repo, "branches(branch3)"), vec![]);
assert_eq!(resolve_commit_ids(mut_repo, "bookmarks(bookmark3)"), vec![]);
assert_eq!(
resolve_commit_ids(mut_repo, "branches(exact:ranch1)"),
resolve_commit_ids(mut_repo, "bookmarks(exact:ookmark1)"),
vec![]
);
// Two branches pointing to the same commit does not result in a duplicate in
// Two bookmarks pointing to the same commit does not result in a duplicate in
// the revset
mut_repo.set_local_branch_target("branch3", RefTarget::normal(commit2.id().clone()));
mut_repo.set_local_bookmark_target("bookmark3", RefTarget::normal(commit2.id().clone()));
assert_eq!(
resolve_commit_ids(mut_repo, "branches()"),
resolve_commit_ids(mut_repo, "bookmarks()"),
vec![commit2.id().clone(), commit1.id().clone()]
);
// Can get branches when there are conflicted refs
mut_repo.set_local_branch_target(
"branch1",
// Can get bookmarks when there are conflicted refs
mut_repo.set_local_bookmark_target(
"bookmark1",
RefTarget::from_legacy_form(
[commit1.id().clone()],
[commit2.id().clone(), commit3.id().clone()],
),
);
mut_repo.set_local_branch_target(
"branch2",
mut_repo.set_local_bookmark_target(
"bookmark2",
RefTarget::from_legacy_form(
[commit2.id().clone()],
[commit3.id().clone(), commit4.id().clone()],
),
);
mut_repo.set_local_branch_target("branch3", RefTarget::absent());
mut_repo.set_local_bookmark_target("bookmark3", RefTarget::absent());
assert_eq!(
resolve_commit_ids(mut_repo, "branches()"),
resolve_commit_ids(mut_repo, "bookmarks()"),
vec![
commit4.id().clone(),
commit3.id().clone(),
@ -2094,7 +2103,7 @@ fn test_evaluate_expression_branches() {
}
#[test]
fn test_evaluate_expression_remote_branches() {
fn test_evaluate_expression_remote_bookmarks() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
@ -2114,11 +2123,11 @@ fn test_evaluate_expression_remote_branches() {
let commit4 = write_random_commit(mut_repo, &settings);
let commit_git_remote = write_random_commit(mut_repo, &settings);
// Can get branches when there are none
assert_eq!(resolve_commit_ids(mut_repo, "remote_branches()"), vec![]);
// Can get bookmarks when there are none
assert_eq!(resolve_commit_ids(mut_repo, "remote_bookmarks()"), vec![]);
// Branch 1 is untracked on remote origin
mut_repo.set_remote_branch(
"branch1",
mut_repo.set_remote_bookmark(
"bookmark1",
"origin",
RemoteRef {
target: RefTarget::normal(commit1.id().clone()),
@ -2126,144 +2135,147 @@ fn test_evaluate_expression_remote_branches() {
},
);
// Branch 2 is tracked on remote private
mut_repo.set_remote_branch(
"branch2",
mut_repo.set_remote_bookmark(
"bookmark2",
"private",
normal_tracking_remote_ref(commit2.id()),
);
// Git-tracking branches aren't included
mut_repo.set_remote_branch(
"branch",
// Git-tracking bookmarks aren't included
mut_repo.set_remote_bookmark(
"bookmark",
git::REMOTE_NAME_FOR_LOCAL_GIT_REPO,
normal_tracking_remote_ref(commit_git_remote.id()),
);
// Can get a few branches
// Can get a few bookmarks
assert_eq!(
resolve_commit_ids(mut_repo, "remote_branches()"),
resolve_commit_ids(mut_repo, "remote_bookmarks()"),
vec![commit2.id().clone(), commit1.id().clone()]
);
// Can get branches with matching names
// Can get bookmarks with matching names
assert_eq!(
resolve_commit_ids(mut_repo, "remote_branches(branch1)"),
resolve_commit_ids(mut_repo, "remote_bookmarks(bookmark1)"),
vec![commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "remote_branches(branch)"),
resolve_commit_ids(mut_repo, "remote_bookmarks(bookmark)"),
vec![commit2.id().clone(), commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "remote_branches(exact:branch1)"),
resolve_commit_ids(mut_repo, "remote_bookmarks(exact:bookmark1)"),
vec![commit1.id().clone()]
);
// Can get branches from matching remotes
// Can get bookmarks from matching remotes
assert_eq!(
resolve_commit_ids(mut_repo, r#"remote_branches("", origin)"#),
resolve_commit_ids(mut_repo, r#"remote_bookmarks("", origin)"#),
vec![commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, r#"remote_branches("", ri)"#),
resolve_commit_ids(mut_repo, r#"remote_bookmarks("", ri)"#),
vec![commit2.id().clone(), commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, r#"remote_branches("", exact:origin)"#),
resolve_commit_ids(mut_repo, r#"remote_bookmarks("", exact:origin)"#),
vec![commit1.id().clone()]
);
// Can get branches with matching names from matching remotes
// Can get bookmarks with matching names from matching remotes
assert_eq!(
resolve_commit_ids(mut_repo, "remote_branches(branch1, ri)"),
resolve_commit_ids(mut_repo, "remote_bookmarks(bookmark1, ri)"),
vec![commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, r#"remote_branches(branch, private)"#),
resolve_commit_ids(mut_repo, r#"remote_bookmarks(bookmark, private)"#),
vec![commit2.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, r#"remote_branches(exact:branch1, exact:origin)"#),
resolve_commit_ids(
mut_repo,
r#"remote_bookmarks(exact:bookmark1, exact:origin)"#
),
vec![commit1.id().clone()]
);
// Can filter branches by tracked and untracked
// Can filter bookmarks by tracked and untracked
assert_eq!(
resolve_commit_ids(mut_repo, "tracked_remote_branches()"),
resolve_commit_ids(mut_repo, "tracked_remote_bookmarks()"),
vec![commit2.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "untracked_remote_branches()"),
resolve_commit_ids(mut_repo, "untracked_remote_bookmarks()"),
vec![commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "untracked_remote_branches(branch1, origin)"),
resolve_commit_ids(mut_repo, "untracked_remote_bookmarks(bookmark1, origin)"),
vec![commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "tracked_remote_branches(branch2, private)"),
resolve_commit_ids(mut_repo, "tracked_remote_bookmarks(bookmark2, private)"),
vec![commit2.id().clone()]
);
// Can silently resolve to an empty set if there's no matches
assert_eq!(
resolve_commit_ids(mut_repo, "remote_branches(branch3)"),
resolve_commit_ids(mut_repo, "remote_bookmarks(bookmark3)"),
vec![]
);
assert_eq!(
resolve_commit_ids(mut_repo, r#"remote_branches("", upstream)"#),
resolve_commit_ids(mut_repo, r#"remote_bookmarks("", upstream)"#),
vec![]
);
assert_eq!(
resolve_commit_ids(mut_repo, r#"remote_branches(branch1, private)"#),
resolve_commit_ids(mut_repo, r#"remote_bookmarks(bookmark1, private)"#),
vec![]
);
assert_eq!(
resolve_commit_ids(mut_repo, r#"remote_branches(exact:ranch1, exact:origin)"#),
resolve_commit_ids(mut_repo, r#"remote_bookmarks(exact:ranch1, exact:origin)"#),
vec![]
);
assert_eq!(
resolve_commit_ids(mut_repo, r#"remote_branches(exact:branch1, exact:orig)"#),
resolve_commit_ids(mut_repo, r#"remote_bookmarks(exact:bookmark1, exact:orig)"#),
vec![]
);
assert_eq!(
resolve_commit_ids(mut_repo, "tracked_remote_branches(branch1)"),
resolve_commit_ids(mut_repo, "tracked_remote_bookmarks(bookmark1)"),
vec![]
);
assert_eq!(
resolve_commit_ids(mut_repo, "untracked_remote_branches(branch2)"),
resolve_commit_ids(mut_repo, "untracked_remote_bookmarks(bookmark2)"),
vec![]
);
// Two branches pointing to the same commit does not result in a duplicate in
// Two bookmarks pointing to the same commit does not result in a duplicate in
// the revset
mut_repo.set_remote_branch(
"branch3",
mut_repo.set_remote_bookmark(
"bookmark3",
"origin",
normal_tracking_remote_ref(commit2.id()),
);
assert_eq!(
resolve_commit_ids(mut_repo, "remote_branches()"),
resolve_commit_ids(mut_repo, "remote_bookmarks()"),
vec![commit2.id().clone(), commit1.id().clone()]
);
// The commits don't have to be in the current set of heads to be included.
mut_repo.remove_head(commit2.id());
assert_eq!(
resolve_commit_ids(mut_repo, "remote_branches()"),
resolve_commit_ids(mut_repo, "remote_bookmarks()"),
vec![commit2.id().clone(), commit1.id().clone()]
);
// Can get branches when there are conflicted refs
mut_repo.set_remote_branch(
"branch1",
// Can get bookmarks when there are conflicted refs
mut_repo.set_remote_bookmark(
"bookmark1",
"origin",
tracking_remote_ref(RefTarget::from_legacy_form(
[commit1.id().clone()],
[commit2.id().clone(), commit3.id().clone()],
)),
);
mut_repo.set_remote_branch(
"branch2",
mut_repo.set_remote_bookmark(
"bookmark2",
"private",
tracking_remote_ref(RefTarget::from_legacy_form(
[commit2.id().clone()],
[commit3.id().clone(), commit4.id().clone()],
)),
);
mut_repo.set_remote_branch("branch3", "origin", RemoteRef::absent());
mut_repo.set_remote_bookmark("bookmark3", "origin", RemoteRef::absent());
assert_eq!(
resolve_commit_ids(mut_repo, "remote_branches()"),
resolve_commit_ids(mut_repo, "remote_bookmarks()"),
vec![
commit4.id().clone(),
commit3.id().clone(),
@ -3513,8 +3525,8 @@ fn test_no_such_revision_suggestion() {
let mut_repo = tx.repo_mut();
let commit = write_random_commit(mut_repo, &settings);
for branch_name in ["foo", "bar", "baz"] {
mut_repo.set_local_branch_target(branch_name, RefTarget::normal(commit.id().clone()));
for bookmark_name in ["foo", "bar", "baz"] {
mut_repo.set_local_bookmark_target(bookmark_name, RefTarget::normal(commit.id().clone()));
}
assert_matches!(resolve_symbol(mut_repo, "bar"), Ok(_));

View file

@ -264,7 +264,7 @@ fn test_rebase_descendants_backward() {
}
#[test]
fn test_rebase_descendants_chain_becomes_branchy() {
fn test_rebase_descendants_chain_becomes_bookmarky() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
@ -972,7 +972,7 @@ fn test_rebase_descendants_contents() {
}
#[test]
fn test_rebase_descendants_basic_branch_update() {
fn test_rebase_descendants_basic_bookmark_update() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
@ -988,7 +988,7 @@ fn test_rebase_descendants_basic_branch_update() {
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.commit_with_parents(&[&commit_a]);
tx.repo_mut()
.set_local_branch_target("main", RefTarget::normal(commit_b.id().clone()));
.set_local_bookmark_target("main", RefTarget::normal(commit_b.id().clone()));
let repo = tx.commit("test");
let mut tx = repo.start_transaction(&settings);
@ -999,7 +999,7 @@ fn test_rebase_descendants_basic_branch_update() {
.unwrap();
tx.repo_mut().rebase_descendants(&settings).unwrap();
assert_eq!(
tx.repo_mut().get_local_branch("main"),
tx.repo_mut().get_local_bookmark("main"),
RefTarget::normal(commit_b2.id().clone())
);
@ -1010,14 +1010,14 @@ fn test_rebase_descendants_basic_branch_update() {
}
#[test]
fn test_rebase_descendants_branch_move_two_steps() {
fn test_rebase_descendants_bookmark_move_two_steps() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
// Branch "main" points to branch C. C gets rewritten as C2 and B gets rewritten
// as B2. C2 should be rebased onto B2, creating C3, and main should be
// updated to point to C3.
// Branch "main" points to bookmark C. C gets rewritten as C2 and B gets
// rewritten as B2. C2 should be rebased onto B2, creating C3, and main
// should be updated to point to C3.
//
// C2 C main C3 main
// | / |
@ -1031,7 +1031,7 @@ fn test_rebase_descendants_branch_move_two_steps() {
let commit_b = graph_builder.commit_with_parents(&[&commit_a]);
let commit_c = graph_builder.commit_with_parents(&[&commit_b]);
tx.repo_mut()
.set_local_branch_target("main", RefTarget::normal(commit_c.id().clone()));
.set_local_bookmark_target("main", RefTarget::normal(commit_c.id().clone()));
let repo = tx.commit("test");
let mut tx = repo.start_transaction(&settings);
@ -1055,19 +1055,19 @@ fn test_rebase_descendants_branch_move_two_steps() {
assert_ne!(commit_c3.id(), commit_c2.id());
assert_eq!(commit_c3.parent_ids(), vec![commit_b2.id().clone()]);
assert_eq!(
tx.repo_mut().get_local_branch("main"),
tx.repo_mut().get_local_bookmark("main"),
RefTarget::normal(commit_c3.id().clone())
);
}
#[test]
fn test_rebase_descendants_basic_branch_update_with_non_local_branch() {
fn test_rebase_descendants_basic_bookmark_update_with_non_local_bookmark() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
// Branch "main" points to commit B. B gets rewritten as B2. Branch main should
// be updated to point to B2. Remote branch main@origin and tag v1 should not
// be updated to point to B2. Remote bookmark main@origin and tag v1 should not
// get updated.
//
// B2 main
@ -1083,9 +1083,9 @@ fn test_rebase_descendants_basic_branch_update_with_non_local_branch() {
state: RemoteRefState::Tracking,
};
tx.repo_mut()
.set_local_branch_target("main", RefTarget::normal(commit_b.id().clone()));
.set_local_bookmark_target("main", RefTarget::normal(commit_b.id().clone()));
tx.repo_mut()
.set_remote_branch("main", "origin", commit_b_remote_ref.clone());
.set_remote_bookmark("main", "origin", commit_b_remote_ref.clone());
tx.repo_mut()
.set_tag_target("v1", RefTarget::normal(commit_b.id().clone()));
let repo = tx.commit("test");
@ -1098,12 +1098,12 @@ fn test_rebase_descendants_basic_branch_update_with_non_local_branch() {
.unwrap();
tx.repo_mut().rebase_descendants(&settings).unwrap();
assert_eq!(
tx.repo_mut().get_local_branch("main"),
tx.repo_mut().get_local_bookmark("main"),
RefTarget::normal(commit_b2.id().clone())
);
// The remote branch and tag should not get updated
// The remote bookmark and tag should not get updated
assert_eq!(
tx.repo_mut().get_remote_branch("main", "origin"),
tx.repo_mut().get_remote_bookmark("main", "origin"),
commit_b_remote_ref,
);
assert_eq!(
@ -1111,8 +1111,8 @@ fn test_rebase_descendants_basic_branch_update_with_non_local_branch() {
RefTarget::normal(commit_b.id().clone())
);
// Commit B is no longer visible even though the remote branch points to it.
// (The user can still see it using e.g. the `remote_branches()` revset.)
// Commit B is no longer visible even though the remote bookmark points to it.
// (The user can still see it using e.g. the `remote_bookmarks()` revset.)
assert_eq!(
*tx.repo_mut().view().heads(),
hashset! {commit_b2.id().clone()}
@ -1120,7 +1120,7 @@ fn test_rebase_descendants_basic_branch_update_with_non_local_branch() {
}
#[test]
fn test_rebase_descendants_update_branch_after_abandon() {
fn test_rebase_descendants_update_bookmark_after_abandon() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
@ -1136,14 +1136,14 @@ fn test_rebase_descendants_update_branch_after_abandon() {
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.commit_with_parents(&[&commit_a]);
tx.repo_mut()
.set_local_branch_target("main", RefTarget::normal(commit_b.id().clone()));
.set_local_bookmark_target("main", RefTarget::normal(commit_b.id().clone()));
let repo = tx.commit("test");
let mut tx = repo.start_transaction(&settings);
tx.repo_mut().record_abandoned_commit(commit_b.id().clone());
tx.repo_mut().rebase_descendants(&settings).unwrap();
assert_eq!(
tx.repo_mut().get_local_branch("main"),
tx.repo_mut().get_local_bookmark("main"),
RefTarget::normal(commit_a.id().clone())
);
@ -1154,7 +1154,7 @@ fn test_rebase_descendants_update_branch_after_abandon() {
}
#[test]
fn test_rebase_descendants_update_branches_after_divergent_rewrite() {
fn test_rebase_descendants_update_bookmarks_after_divergent_rewrite() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
@ -1176,9 +1176,9 @@ fn test_rebase_descendants_update_branches_after_divergent_rewrite() {
let commit_b = graph_builder.commit_with_parents(&[&commit_a]);
let commit_c = graph_builder.commit_with_parents(&[&commit_b]);
tx.repo_mut()
.set_local_branch_target("main", RefTarget::normal(commit_b.id().clone()));
.set_local_bookmark_target("main", RefTarget::normal(commit_b.id().clone()));
tx.repo_mut()
.set_local_branch_target("other", RefTarget::normal(commit_c.id().clone()));
.set_local_bookmark_target("other", RefTarget::normal(commit_c.id().clone()));
let repo = tx.commit("test");
let mut tx = repo.start_transaction(&settings);
@ -1226,7 +1226,7 @@ fn test_rebase_descendants_update_branches_after_divergent_rewrite() {
);
tx.repo_mut().rebase_descendants(&settings).unwrap();
let main_target = tx.repo_mut().get_local_branch("main");
let main_target = tx.repo_mut().get_local_bookmark("main");
assert!(main_target.has_conflict());
// If the branch were moved at each rewrite point, there would be separate
// negative terms: { commit_b => 2, commit_b4 => 1 }. Since we flatten
@ -1245,7 +1245,7 @@ fn test_rebase_descendants_update_branches_after_divergent_rewrite() {
},
);
let other_target = tx.repo_mut().get_local_branch("other");
let other_target = tx.repo_mut().get_local_bookmark("other");
assert_eq!(other_target.as_normal(), Some(commit_c.id()));
assert_eq!(
@ -1261,20 +1261,20 @@ fn test_rebase_descendants_update_branches_after_divergent_rewrite() {
}
#[test]
fn test_rebase_descendants_rewrite_updates_branch_conflict() {
fn test_rebase_descendants_rewrite_updates_bookmark_conflict() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
// Branch "main" is a conflict removing commit A and adding commits B and C.
// A gets rewritten as A2 and A3. B gets rewritten as B2 and B2. The branch
// A gets rewritten as A2 and A3. B gets rewritten as B2 and B2. The bookmark
// should become a conflict removing A and B, and adding B2, B3, C.
let mut tx = repo.start_transaction(&settings);
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.repo_mut());
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.initial_commit();
let commit_c = graph_builder.initial_commit();
tx.repo_mut().set_local_branch_target(
tx.repo_mut().set_local_bookmark_target(
"main",
RefTarget::from_legacy_form(
[commit_a.id().clone()],
@ -1318,7 +1318,7 @@ fn test_rebase_descendants_rewrite_updates_branch_conflict() {
);
tx.repo_mut().rebase_descendants(&settings).unwrap();
let target = tx.repo_mut().get_local_branch("main");
let target = tx.repo_mut().get_local_bookmark("main");
assert!(target.has_conflict());
assert_eq!(
target.removed_ids().counts(),
@ -1346,7 +1346,7 @@ fn test_rebase_descendants_rewrite_updates_branch_conflict() {
}
#[test]
fn test_rebase_descendants_rewrite_resolves_branch_conflict() {
fn test_rebase_descendants_rewrite_resolves_bookmark_conflict() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
@ -1363,7 +1363,7 @@ fn test_rebase_descendants_rewrite_resolves_branch_conflict() {
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.commit_with_parents(&[&commit_a]);
let commit_c = graph_builder.commit_with_parents(&[&commit_a]);
tx.repo_mut().set_local_branch_target(
tx.repo_mut().set_local_bookmark_target(
"main",
RefTarget::from_legacy_form(
[commit_a.id().clone()],
@ -1381,7 +1381,7 @@ fn test_rebase_descendants_rewrite_resolves_branch_conflict() {
.unwrap();
tx.repo_mut().rebase_descendants(&settings).unwrap();
assert_eq!(
tx.repo_mut().get_local_branch("main"),
tx.repo_mut().get_local_bookmark("main"),
RefTarget::normal(commit_b2.id().clone())
);
@ -1392,21 +1392,21 @@ fn test_rebase_descendants_rewrite_resolves_branch_conflict() {
}
#[test]
fn test_rebase_descendants_branch_delete_modify_abandon() {
fn test_rebase_descendants_bookmark_delete_modify_abandon() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
// Branch "main" initially points to commit A. One operation rewrites it to
// point to B (child of A). A concurrent operation deletes the branch. That
// leaves the branch pointing to "-A+B". We now abandon B. That should
// result in the branch pointing to "-A+A=0", so the branch should
// point to B (child of A). A concurrent operation deletes the bookmark. That
// leaves the bookmark pointing to "-A+B". We now abandon B. That should
// result in the bookmark pointing to "-A+A=0", so the bookmark should
// be deleted.
let mut tx = repo.start_transaction(&settings);
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.repo_mut());
let commit_a = graph_builder.initial_commit();
let commit_b = graph_builder.commit_with_parents(&[&commit_a]);
tx.repo_mut().set_local_branch_target(
tx.repo_mut().set_local_bookmark_target(
"main",
RefTarget::from_legacy_form([commit_a.id().clone()], [commit_b.id().clone()]),
);
@ -1415,7 +1415,14 @@ fn test_rebase_descendants_branch_delete_modify_abandon() {
let mut tx = repo.start_transaction(&settings);
tx.repo_mut().record_abandoned_commit(commit_b.id().clone());
tx.repo_mut().rebase_descendants(&settings).unwrap();
assert_eq!(tx.repo_mut().get_local_branch("main"), RefTarget::absent());
assert_eq!(
tx.repo_mut().get_local_bookmark("main"),
RefTarget::absent()
);
assert_eq!(
tx.repo_mut().get_local_bookmark("main"),
RefTarget::absent()
);
}
#[test]

View file

@ -202,8 +202,8 @@ fn test_merge_views_checkout() {
}
#[test]
fn test_merge_views_branches() {
// Tests merging of branches (by performing divergent operations). See
fn test_merge_views_bookmarks() {
// Tests merging of bookmarks (by performing concurrent operations). See
// test_refs.rs for tests of merging of individual ref targets.
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
@ -211,84 +211,87 @@ fn test_merge_views_branches() {
let mut tx = repo.start_transaction(&settings);
let mut_repo = tx.repo_mut();
let main_branch_local_tx0 = write_random_commit(mut_repo, &settings);
let main_branch_origin_tx0 = write_random_commit(mut_repo, &settings);
let main_branch_alternate_tx0 = write_random_commit(mut_repo, &settings);
let main_branch_origin_tx0_remote_ref = RemoteRef {
target: RefTarget::normal(main_branch_origin_tx0.id().clone()),
let main_bookmark_local_tx0 = write_random_commit(mut_repo, &settings);
let main_bookmark_origin_tx0 = write_random_commit(mut_repo, &settings);
let main_bookmark_alternate_tx0 = write_random_commit(mut_repo, &settings);
let main_bookmark_origin_tx0_remote_ref = RemoteRef {
target: RefTarget::normal(main_bookmark_origin_tx0.id().clone()),
state: RemoteRefState::New,
};
let main_branch_alternate_tx0_remote_ref = RemoteRef {
target: RefTarget::normal(main_branch_alternate_tx0.id().clone()),
let main_bookmark_alternate_tx0_remote_ref = RemoteRef {
target: RefTarget::normal(main_bookmark_alternate_tx0.id().clone()),
state: RemoteRefState::Tracking,
};
mut_repo.set_local_branch_target(
mut_repo.set_local_bookmark_target(
"main",
RefTarget::normal(main_branch_local_tx0.id().clone()),
RefTarget::normal(main_bookmark_local_tx0.id().clone()),
);
mut_repo.set_remote_branch("main", "origin", main_branch_origin_tx0_remote_ref);
mut_repo.set_remote_branch(
mut_repo.set_remote_bookmark("main", "origin", main_bookmark_origin_tx0_remote_ref);
mut_repo.set_remote_bookmark(
"main",
"alternate",
main_branch_alternate_tx0_remote_ref.clone(),
main_bookmark_alternate_tx0_remote_ref.clone(),
);
let feature_branch_local_tx0 = write_random_commit(mut_repo, &settings);
mut_repo.set_local_branch_target(
let feature_bookmark_local_tx0 = write_random_commit(mut_repo, &settings);
mut_repo.set_local_bookmark_target(
"feature",
RefTarget::normal(feature_branch_local_tx0.id().clone()),
RefTarget::normal(feature_bookmark_local_tx0.id().clone()),
);
let repo = tx.commit("test");
let mut tx1 = repo.start_transaction(&settings);
let main_branch_local_tx1 = write_random_commit(tx1.repo_mut(), &settings);
tx1.repo_mut().set_local_branch_target(
let main_bookmark_local_tx1 = write_random_commit(tx1.repo_mut(), &settings);
tx1.repo_mut().set_local_bookmark_target(
"main",
RefTarget::normal(main_branch_local_tx1.id().clone()),
RefTarget::normal(main_bookmark_local_tx1.id().clone()),
);
let feature_branch_tx1 = write_random_commit(tx1.repo_mut(), &settings);
tx1.repo_mut().set_local_branch_target(
let feature_bookmark_tx1 = write_random_commit(tx1.repo_mut(), &settings);
tx1.repo_mut().set_local_bookmark_target(
"feature",
RefTarget::normal(feature_branch_tx1.id().clone()),
RefTarget::normal(feature_bookmark_tx1.id().clone()),
);
let mut tx2 = repo.start_transaction(&settings);
let main_branch_local_tx2 = write_random_commit(tx2.repo_mut(), &settings);
let main_branch_origin_tx2 = write_random_commit(tx2.repo_mut(), &settings);
let main_branch_origin_tx2_remote_ref = RemoteRef {
target: RefTarget::normal(main_branch_origin_tx2.id().clone()),
let main_bookmark_local_tx2 = write_random_commit(tx2.repo_mut(), &settings);
let main_bookmark_origin_tx2 = write_random_commit(tx2.repo_mut(), &settings);
let main_bookmark_origin_tx2_remote_ref = RemoteRef {
target: RefTarget::normal(main_bookmark_origin_tx2.id().clone()),
state: RemoteRefState::Tracking,
};
tx2.repo_mut().set_local_branch_target(
tx2.repo_mut().set_local_bookmark_target(
"main",
RefTarget::normal(main_branch_local_tx2.id().clone()),
RefTarget::normal(main_bookmark_local_tx2.id().clone()),
);
tx2.repo_mut().set_remote_bookmark(
"main",
"origin",
main_bookmark_origin_tx2_remote_ref.clone(),
);
tx2.repo_mut()
.set_remote_branch("main", "origin", main_branch_origin_tx2_remote_ref.clone());
let repo = commit_transactions(&settings, vec![tx1, tx2]);
let expected_main_branch = BranchTarget {
let expected_main_bookmark = BranchTarget {
local_target: &RefTarget::from_legacy_form(
[main_branch_local_tx0.id().clone()],
[main_bookmark_local_tx0.id().clone()],
[
main_branch_local_tx1.id().clone(),
main_branch_local_tx2.id().clone(),
main_bookmark_local_tx1.id().clone(),
main_bookmark_local_tx2.id().clone(),
],
),
remote_refs: vec![
("alternate", &main_branch_alternate_tx0_remote_ref),
("alternate", &main_bookmark_alternate_tx0_remote_ref),
// tx1: unchanged, tx2: new -> tracking
("origin", &main_branch_origin_tx2_remote_ref),
("origin", &main_bookmark_origin_tx2_remote_ref),
],
};
let expected_feature_branch = BranchTarget {
local_target: &RefTarget::normal(feature_branch_tx1.id().clone()),
let expected_feature_bookmark = BranchTarget {
local_target: &RefTarget::normal(feature_bookmark_tx1.id().clone()),
remote_refs: vec![],
};
assert_eq!(
repo.view().branches().collect::<BTreeMap<_, _>>(),
repo.view().bookmarks().collect::<BTreeMap<_, _>>(),
btreemap! {
"main" => expected_main_branch,
"feature" => expected_feature_branch,
"main" => expected_main_bookmark,
"feature" => expected_feature_bookmark,
}
);
}
@ -347,48 +350,51 @@ fn test_merge_views_git_refs() {
let mut tx = repo.start_transaction(&settings);
let mut_repo = tx.repo_mut();
let main_branch_tx0 = write_random_commit(mut_repo, &settings);
let main_bookmark_tx0 = write_random_commit(mut_repo, &settings);
mut_repo.set_git_ref_target(
"refs/heads/main",
RefTarget::normal(main_branch_tx0.id().clone()),
RefTarget::normal(main_bookmark_tx0.id().clone()),
);
let feature_branch_tx0 = write_random_commit(mut_repo, &settings);
let feature_bookmark_tx0 = write_random_commit(mut_repo, &settings);
mut_repo.set_git_ref_target(
"refs/heads/feature",
RefTarget::normal(feature_branch_tx0.id().clone()),
RefTarget::normal(feature_bookmark_tx0.id().clone()),
);
let repo = tx.commit("test");
let mut tx1 = repo.start_transaction(&settings);
let main_branch_tx1 = write_random_commit(tx1.repo_mut(), &settings);
let main_bookmark_tx1 = write_random_commit(tx1.repo_mut(), &settings);
tx1.repo_mut().set_git_ref_target(
"refs/heads/main",
RefTarget::normal(main_branch_tx1.id().clone()),
RefTarget::normal(main_bookmark_tx1.id().clone()),
);
let feature_branch_tx1 = write_random_commit(tx1.repo_mut(), &settings);
let feature_bookmark_tx1 = write_random_commit(tx1.repo_mut(), &settings);
tx1.repo_mut().set_git_ref_target(
"refs/heads/feature",
RefTarget::normal(feature_branch_tx1.id().clone()),
RefTarget::normal(feature_bookmark_tx1.id().clone()),
);
let mut tx2 = repo.start_transaction(&settings);
let main_branch_tx2 = write_random_commit(tx2.repo_mut(), &settings);
let main_bookmark_tx2 = write_random_commit(tx2.repo_mut(), &settings);
tx2.repo_mut().set_git_ref_target(
"refs/heads/main",
RefTarget::normal(main_branch_tx2.id().clone()),
RefTarget::normal(main_bookmark_tx2.id().clone()),
);
let repo = commit_transactions(&settings, vec![tx1, tx2]);
let expected_main_branch = RefTarget::from_legacy_form(
[main_branch_tx0.id().clone()],
[main_branch_tx1.id().clone(), main_branch_tx2.id().clone()],
let expected_main_bookmark = RefTarget::from_legacy_form(
[main_bookmark_tx0.id().clone()],
[
main_bookmark_tx1.id().clone(),
main_bookmark_tx2.id().clone(),
],
);
let expected_feature_branch = RefTarget::normal(feature_branch_tx1.id().clone());
let expected_feature_bookmark = RefTarget::normal(feature_bookmark_tx1.id().clone());
assert_eq!(
repo.view().git_refs(),
&btreemap! {
"refs/heads/main".to_string() => expected_main_branch,
"refs/heads/feature".to_string() => expected_feature_branch,
"refs/heads/main".to_string() => expected_main_bookmark,
"refs/heads/feature".to_string() => expected_feature_bookmark,
}
);
}

View file

@ -103,7 +103,7 @@ nav:
- Concepts:
- 'Working Copy': 'working-copy.md'
- 'Branches': 'branches.md'
- 'Bookmarks': 'bookmarks.md'
- 'Conflicts': 'conflicts.md'
- 'Operation Log': 'operation-log.md'
- 'Glossary': 'glossary.md'