Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions lore-revision/src/revision/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ use crate::repository::RepositoryContext;
use crate::repository::RepositoryWriteToken;
use crate::repository::THEIRS_SUFFIX;
use crate::revision;
use crate::revision::ResolveSearchLocation;
use crate::state;
use crate::state::State;
use crate::util;
Expand Down Expand Up @@ -325,6 +326,37 @@ pub async fn sync(
.await
.forward::<SyncError>("Failed to find revision")?;
lore_debug!("Sync resolved revision target is {revision}");

// Determine location for the explicitly-provided revision.
// When the search mode includes remote (--remote or default),
// check whether the resolved revision is on the remote timeline.
// Without this, location stays Local and the local branch Latest
// pointer is never updated, leaving status reporting the branch
// as behind remote.
if !matches!(
execution_context().globals().search_location(),
ResolveSearchLocation::Local
) {
if let Ok(remote) = repository.remote().await {
remote_latest =
branch::load_remote_latest(remote.clone(), repository.id, branch_id)
.await
.unwrap_or_default();
}
if !remote_latest.is_zero() {
if revision == remote_latest {
location = LoreBranchLocation::Remote;
} else if let Ok((_bp, _remote_history, local_history)) =
history::find_branch_point(repository.clone(), remote_latest, revision).await
{
// revision is an ancestor of remote_latest (on the
// remote timeline) when local_history is empty.
if local_history.is_empty() {
location = LoreBranchLocation::Remote;
}
}
}
}
} else {
// If there is no revision given, then we determine if the local and remote
// latest revisions are in line or divergent.
Expand Down
48 changes: 48 additions & 0 deletions scripts/test/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,3 +659,51 @@ def test_sync_far_behind_with_local_merge_tip_and_remote_merges(new_lore_repo):
sync_output = repo.sync()
_assert_clean_fast_forward(sync_output)
repo.repository_verify()


@pytest.mark.smoke
def test_sync_remote_explicit_revision(new_lore_repo):
"""
Test that syncing to an explicit revision with --remote updates the
local branch Latest pointer, preventing 'Local branch is behind remote'
status.
"""
repo = new_lore_repo()

# Create some initial history
with repo.open_file("file1.txt", "w+") as f:
f.write("v1")
repo.stage("file1.txt")
repo.commit("Commit 1")

# Create a side branch
repo.branch_create("feature")
repo.branch_switch("feature")
with repo.open_file("file2.txt", "w+") as f:
f.write("v1")
repo.stage("file2.txt")
repo.commit("Feature commit")
repo.push()

# Switch back to main, merge the feature branch
repo.branch_switch("main")
repo.branch_merge_start("feature", message="Merge feature into main")
repo.push()

# Get the client to a clean state on an older commit
clone = repo.clone()
clone.sync("@LATEST~1")
# Get the latest revision hash from the remote by running branch_latest
import re
merge_revision_output = repo.run(["status"])
merge_revision = re.search(r'revision \d+ -> ([a-f0-9]+)', merge_revision_output).group(1)
# Now simulate the bug: sync explicitly to the merge revision with --remote
clone.sync(merge_revision, remote=True)

# Check that status does NOT complain about being behind remote
status_output = clone.status()
assert "Local branch is behind remote" not in status_output, "Branch Latest pointer was not updated"

# Second sync should say Already on branch, without actually syncing anything
sync_output = clone.sync()
assert "Already on branch" in sync_output, "Sync did not realize we are already at latest"