Skip to content

Commit 225d6ef

Browse files
committed
updates
1 parent 818c4e7 commit 225d6ef

File tree

3 files changed

+126
-105
lines changed

3 files changed

+126
-105
lines changed

src/git.rs

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ pub(crate) fn git_branch_status(
137137
None => git_remote_main(DEFAULT_REMOTE)?,
138138
};
139139
let is_descendent = exists && is_ancestor(&parent_branch, branch)?;
140-
let upstream_symbolic_name = git_get_upstream(branch).ok();
140+
let upstream_symbolic_name = git_get_upstream(branch);
141141
let upstream_synced = upstream_symbolic_name
142142
.as_ref()
143143
.is_some_and(|upstream| shas_match(upstream, branch));
@@ -172,34 +172,23 @@ pub(crate) fn git_checkout_main(new_branch: Option<&str>) -> Result<()> {
172172
git_fetch()?;
173173
// Assuming the dominant remote is "origin".
174174
// TODO: add support for different remotes.
175-
let remote = "origin";
175+
let remote = DEFAULT_REMOTE;
176176
let trunk = git_trunk()?;
177-
// Get the HEAD ref of the remote.
178-
let remote_main = git_remote_main(remote)?;
179-
// Figure out the branch name.
180-
let main_branch = after_text(&remote_main, format!("{remote}/"))
181-
.ok_or(anyhow!("no branch?"))?
182-
.to_string();
183177

184178
// Check that we don't orphan unpushed changes in the local `main` branch.
185-
if !is_ancestor(&main_branch, &remote_main)? {
179+
if !is_ancestor(&trunk.main_branch, &trunk.remote_main)? {
186180
bail!("It looks like this would orphan unpushed changes in your main branch! Aborting...");
187181
}
188182

189183
// Repoint "main" to the remote main branch.
190184
run_git(&[
191185
"branch",
192186
"-f",
193-
&main_branch,
194-
&format!("{}/{}", remote, main_branch),
187+
&trunk.main_branch,
188+
&format!("{}/{}", remote, trunk.main_branch),
195189
])?;
196190
if let Some(new_branch) = new_branch {
197-
run_git(&[
198-
"checkout",
199-
"-B",
200-
new_branch,
201-
&format!("{}/{}", remote, main_branch),
202-
])?;
191+
run_git(&["checkout", "-B", new_branch, &trunk.remote_main])?;
203192
}
204193
Ok(())
205194
}
@@ -225,16 +214,20 @@ pub(crate) fn git_remote_main(remote: &str) -> Result<String> {
225214
.output()
226215
.map(|s| s.trim().to_string())
227216
.ok_or(anyhow!("No remote main branch?"))
217+
.and_then(|s| {
218+
Ok(after_text(s.trim(), "refs/remotes/")
219+
.ok_or(anyhow!("no refs/remotes/ prefix?"))?
220+
.to_string())
221+
})
228222
}
229223

230-
pub(crate) fn git_get_upstream(branch: &str) -> Result<String> {
224+
pub(crate) fn git_get_upstream(branch: &str) -> Option<String> {
231225
run_git(&[
232226
"rev-parse",
233227
"--abbrev-ref",
234228
"--symbolic-full-name",
235229
&format!("{branch}@{{upstream}}"),
236-
])?
237-
.output()
238-
.map(|s| s.trim().to_string())
239-
.ok_or(anyhow!("No upstream branch?"))
230+
])
231+
.ok()
232+
.and_then(|s| s.output())
240233
}

src/main.rs

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use anyhow::{Context, Result, anyhow, bail, ensure};
77
use clap::{Parser, Subcommand};
88
use git::{
99
DEFAULT_REMOTE, GitBranchStatus, after_text, git_branch_status, git_checkout_main, git_fetch,
10-
git_remote_main, git_sha, is_ancestor, run_git_status, shas_match,
10+
git_get_upstream, git_remote_main, git_sha, is_ancestor, run_git_status, shas_match,
1111
};
12-
use state::{Branch, RebaseStep, load_state, save_state};
12+
use state::{Branch, RebaseStep};
1313
use std::env;
1414
use std::fs::canonicalize;
1515
use tracing::level_filters::LevelFilter;
@@ -36,20 +36,28 @@ struct Args {
3636
#[derive(Subcommand)]
3737
enum Command {
3838
/// Show the status of the git-stack tree in the current repo. This is the default command when
39-
/// a command is omitted. (ie: `git stack` is the same as `git stack status`)
40-
Status,
39+
/// one is omitted. (ie: `git stack` is the same as `git stack status`)
40+
Status {
41+
/// Whether to fetch the latest changes from the remote before showing the status.
42+
#[arg(long, short, default_value_t = false)]
43+
fetch: bool,
44+
},
4145
/// Restack your active branch and all branches in its related stack.
4246
Restack {
4347
/// The name of the branch to restack.
4448
#[arg(long, short)]
4549
branch: Option<String>,
50+
/// Push any changes up to the remote after restacking.
51+
#[arg(long, short)]
52+
push: bool,
4653
},
4754
/// Shows the log between the given branch and its parent (git-stack tree) branch.
4855
Log {
4956
/// Specifies the branch whose log should be shown. If omitted, the current branch will
5057
/// be used.
5158
branch: Option<String>,
5259
},
60+
/// Show or edit per-branch notes.
5361
Note {
5462
#[arg(long, short, default_value_t = false)]
5563
edit: bool,
@@ -96,7 +104,12 @@ fn inner_main() -> Result<()> {
96104

97105
tracing_subscriber::registry()
98106
// We don't need timestamps in the logs.
99-
.with(tracing_subscriber::fmt::layer().without_time())
107+
.with(
108+
tracing_subscriber::fmt::layer()
109+
.with_file(true)
110+
.with_line_number(true)
111+
.without_time(),
112+
)
100113
// Allow usage of RUST_LOG environment variable to set the log level.
101114
.with(
102115
tracing_subscriber::EnvFilter::builder()
@@ -112,7 +125,7 @@ fn inner_main() -> Result<()> {
112125
.into_string()
113126
.map_err(|error| anyhow!("Invalid git directory: '{}'", error.to_string_lossy()))?;
114127

115-
let mut state = load_state().context("loading state")?;
128+
let mut state = State::load_state().context("loading state")?;
116129
state.refresh_lkgs(&repo)?;
117130

118131
tracing::debug!("Current directory: {}", repo);
@@ -121,22 +134,20 @@ fn inner_main() -> Result<()> {
121134
let current_branch = run_git(&["rev-parse", "--abbrev-ref", "HEAD"])?
122135
.output()
123136
.ok_or(anyhow!("No current branch?"))?;
124-
let current_upstream = run_git(&["rev-parse", "--abbrev-ref", "@{upstream}"])
125-
.ok()
126-
.and_then(|out| out.output());
137+
let current_upstream = git_get_upstream("");
127138
tracing::debug!(run_version, current_branch, current_upstream);
128139

129140
match args.command {
130141
Some(Command::Checkout { branch_name }) => {
131142
state.checkout(&repo, current_branch, current_upstream, branch_name)
132143
}
133-
Some(Command::Restack { branch }) => {
134-
restack(state, &repo, run_version, branch, current_branch)
144+
Some(Command::Restack { branch, push }) => {
145+
restack(state, &repo, run_version, branch, current_branch, push)
135146
}
136147
Some(Command::Mount { parent_branch }) => {
137148
state.mount(&repo, &current_branch, parent_branch)
138149
}
139-
Some(Command::Status) | None => status(state, &repo, &current_branch),
150+
Some(Command::Status { fetch }) => status(state, &repo, &current_branch, fetch),
140151
Some(Command::Delete { branch_name }) => state.delete_branch(&repo, &branch_name),
141152
Some(Command::Diff { branch }) => diff(state, &repo, &branch.unwrap_or(current_branch)),
142153
Some(Command::Log { branch }) => show_log(state, &repo, &branch.unwrap_or(current_branch)),
@@ -148,6 +159,7 @@ fn inner_main() -> Result<()> {
148159
state.show_note(&repo, &branch)
149160
}
150161
}
162+
None => status(state, &repo, &current_branch, false),
151163
}
152164
}
153165

@@ -237,10 +249,11 @@ fn recur_tree(
237249

238250
println!(
239251
"{} ({}) {}{}{}",
240-
if is_current_branch {
241-
branch.name.truecolor(142, 192, 124).bold()
242-
} else {
243-
branch.name.truecolor(178, 178, 178)
252+
match (is_current_branch, branch_status.is_descendent) {
253+
(true, true) => branch.name.truecolor(142, 192, 124).bold(),
254+
(true, false) => branch.name.truecolor(215, 153, 33).bold(),
255+
(false, true) => branch.name.truecolor(142, 192, 124),
256+
(false, false) => branch.name.truecolor(215, 153, 33),
244257
},
245258
branch_status.sha[..8].truecolor(215, 153, 33),
246259
{
@@ -314,8 +327,11 @@ fn recur_tree(
314327
Ok(())
315328
}
316329

317-
fn status(state: State, repo: &str, orig_branch: &str) -> Result<()> {
318-
git_fetch()?;
330+
fn status(mut state: State, repo: &str, orig_branch: &str, fetch: bool) -> Result<()> {
331+
if fetch {
332+
git_fetch()?;
333+
}
334+
let trunk = state.ensure_trunk(repo)?;
319335

320336
let Some(tree) = state.get_tree(repo) else {
321337
eprintln!(
@@ -332,6 +348,7 @@ fn status(state: State, repo: &str, orig_branch: &str) -> Result<()> {
332348
);
333349
eprintln!("Run `git stack mount <parent_branch>` to add it.");
334350
}
351+
state.save_state()?;
335352
Ok(())
336353
}
337354

@@ -341,34 +358,35 @@ fn restack(
341358
run_version: String,
342359
restack_branch: Option<String>,
343360
orig_branch: String,
361+
push: bool,
344362
) -> Result<(), anyhow::Error> {
345363
let restack_branch = restack_branch.unwrap_or(orig_branch.clone());
346364

347365
// Find starting_branch in the stacks of branches to determine which stack to use.
348366
let plan = state.plan_restack(repo, &restack_branch)?;
349367

350-
tracing::info!(?plan, "Restacking branches with plan...");
368+
tracing::debug!(?plan, "Restacking branches with plan...");
351369
git_checkout_main(None)?;
352370
for RebaseStep { parent, branch } in plan {
353-
tracing::info!(
371+
tracing::debug!(
354372
"Starting branch: {} [pwd={}]",
355373
restack_branch,
356374
env::current_dir()?.display()
357375
);
358376
let source = git_sha(&branch.name)?;
359377

360378
if is_ancestor(&parent, &branch.name)? {
361-
tracing::info!(
379+
tracing::debug!(
362380
"Branch '{}' is already stacked on '{}'.",
363381
branch.name,
364382
parent
365383
);
366-
tracing::info!("Force-pushing '{}' to origin...", branch.name);
367-
if !shas_match(&format!("origin/{}", branch.name), &branch.name) {
384+
tracing::debug!("Force-pushing '{}' to {DEFAULT_REMOTE}...", branch.name);
385+
if push && !shas_match(&format!("{DEFAULT_REMOTE}/{}", branch.name), &branch.name) {
368386
run_git(&[
369387
"push",
370388
"-fu",
371-
"origin",
389+
DEFAULT_REMOTE,
372390
&format!("{branch_name}:{branch_name}", branch_name = branch.name),
373391
])?;
374392
}
@@ -404,7 +422,9 @@ fn restack(
404422
);
405423
std::process::exit(1);
406424
}
407-
git_push(&branch.name)?;
425+
if push {
426+
git_push(&branch.name)?;
427+
}
408428
continue;
409429
} else {
410430
tracing::info!(
@@ -425,7 +445,9 @@ fn restack(
425445
);
426446
std::process::exit(1);
427447
}
428-
git_push(&branch.name)?;
448+
if push {
449+
git_push(&branch.name)?;
450+
}
429451
tracing::info!("Rebase completed successfully. Continuing...");
430452
}
431453
}
@@ -441,8 +463,13 @@ fn restack(
441463
}
442464

443465
fn git_push(branch: &str) -> Result<()> {
444-
if !shas_match(&format!("origin/{}", branch), branch) {
445-
run_git(&["push", "-fu", "origin", &format!("{}:{}", branch, branch)])?;
466+
if !shas_match(&format!("{DEFAULT_REMOTE}/{}", branch), branch) {
467+
run_git(&[
468+
"push",
469+
"-fu",
470+
DEFAULT_REMOTE,
471+
&format!("{}:{}", branch, branch),
472+
])?;
446473
}
447474
Ok(())
448475
}

0 commit comments

Comments
 (0)