git_workty/commands/
sync.rs1use crate::git::GitRepo;
2use crate::status::{get_all_statuses, is_worktree_dirty};
3use crate::ui;
4use crate::worktree::list_worktrees;
5use anyhow::{Context, Result};
6use std::process::Command;
7
8pub struct SyncOptions {
9 pub dry_run: bool,
10 pub fetch: bool,
11}
12
13pub fn execute(repo: &GitRepo, opts: SyncOptions) -> Result<()> {
14 if opts.fetch {
16 ui::print_info("Fetching from origin...");
17 let output = Command::new("git")
18 .current_dir(&repo.root)
19 .args(["fetch", "--prune", "origin"])
20 .output()
21 .context("Failed to fetch")?;
22
23 if !output.status.success() {
24 ui::print_warning("Failed to fetch from origin");
25 }
26 }
27
28 let worktrees = list_worktrees(repo)?;
29 let statuses = get_all_statuses(repo, &worktrees);
30
31 let mut synced = 0;
32 let mut skipped_dirty = 0;
33 let mut skipped_no_upstream = 0;
34 let mut failed = 0;
35
36 for (wt, status) in &statuses {
37 if wt.is_main_worktree(repo) {
39 continue;
40 }
41
42 if wt.detached {
44 continue;
45 }
46
47 if status.upstream.is_none() {
49 skipped_no_upstream += 1;
50 continue;
51 }
52
53 let behind = status.behind.unwrap_or(0);
55 if behind == 0 {
56 continue;
57 }
58
59 let branch_name = wt.name();
60
61 if is_worktree_dirty(wt) {
63 if !opts.dry_run {
64 ui::print_warning(&format!("{}: skipped (dirty)", branch_name));
65 }
66 skipped_dirty += 1;
67 continue;
68 }
69
70 if opts.dry_run {
71 ui::print_info(&format!(
72 "{}: would rebase ({} commits behind)",
73 branch_name, behind
74 ));
75 synced += 1;
76 continue;
77 }
78
79 ui::print_info(&format!("{}: rebasing...", branch_name));
81
82 let output = Command::new("git")
83 .current_dir(&wt.path)
84 .args(["rebase"])
85 .output()
86 .context("Failed to run git rebase")?;
87
88 if output.status.success() {
89 ui::print_success(&format!("{}: rebased", branch_name));
90 synced += 1;
91 } else {
92 let _ = Command::new("git")
94 .current_dir(&wt.path)
95 .args(["rebase", "--abort"])
96 .output();
97
98 ui::print_warning(&format!("{}: rebase failed, aborted", branch_name));
99 failed += 1;
100 }
101 }
102
103 println!();
105 if opts.dry_run {
106 ui::print_info(&format!("Would sync {} worktree(s)", synced));
107 } else {
108 ui::print_info(&format!(
109 "Synced: {}, Skipped (dirty): {}, Skipped (no upstream): {}, Failed: {}",
110 synced, skipped_dirty, skipped_no_upstream, failed
111 ));
112 }
113
114 Ok(())
115}