Skip to content

Commit 97df48b

Browse files
committed
wip
1 parent 94599f4 commit 97df48b

File tree

3 files changed

+11
-301
lines changed

3 files changed

+11
-301
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ repository = "https://github.com/wbbradley/git-stack"
1414
anyhow = { version = "1.0.98", features = ["backtrace"] }
1515
chrono = "0.4.41"
1616
clap = { version = "4.5.37", features = ["derive"] }
17+
clap_complete = "4.5.65"
1718
colored = "3.0.0"
1819
crossterm = "0.29.0"
1920
git2 = "0.20"

src/main.rs

Lines changed: 0 additions & 301 deletions
Original file line numberDiff line numberDiff line change
@@ -142,16 +142,6 @@ enum Command {
142142
#[command(subcommand)]
143143
action: CacheAction,
144144
},
145-
/// Import branch stack from existing GitHub PRs.
146-
/// Reconstructs the git-stack tree by walking the PR base chain.
147-
Import {
148-
/// Branch to import (defaults to current branch)
149-
#[arg(long, short)]
150-
branch: Option<String>,
151-
/// Import all open PRs for the repo, not just the current branch's chain
152-
#[arg(long, short)]
153-
all: bool,
154-
},
155145
/// Sync local git-stack state with GitHub PRs.
156146
/// Default: weak push then weak pull (bidirectional sync).
157147
Sync {
@@ -396,10 +386,6 @@ fn inner_main() -> Result<()> {
396386
Some(Command::Cache { action }) => {
397387
handle_cache_command(&git_repo, &mut state, &repo, action)
398388
}
399-
Some(Command::Import { branch, all }) => {
400-
let branch = branch.unwrap_or(current_branch);
401-
handle_import_command(&git_repo, &mut state, &repo, &branch, all)
402-
}
403389
Some(Command::Sync {
404390
push,
405391
pull,
@@ -1233,293 +1219,6 @@ fn git_push(git_repo: &GitRepo, branch: &str) -> Result<()> {
12331219
Ok(())
12341220
}
12351221

1236-
// ============== GitHub Import Command ==============
1237-
1238-
fn handle_import_command(
1239-
git_repo: &GitRepo,
1240-
state: &mut State,
1241-
repo: &str,
1242-
branch: &str,
1243-
import_all: bool,
1244-
) -> Result<()> {
1245-
use github::{
1246-
GitHubClient,
1247-
get_repo_identifier,
1248-
has_github_token,
1249-
setup_github_token_interactive,
1250-
};
1251-
1252-
let repo_id = get_repo_identifier(git_repo)?;
1253-
1254-
// Ensure we have auth configured
1255-
if !has_github_token(&repo_id.host) {
1256-
println!("{}", "GitHub authentication required.".yellow());
1257-
setup_github_token_interactive()?;
1258-
}
1259-
1260-
let client = GitHubClient::from_env(&repo_id)?;
1261-
let trunk = crate::git::git_trunk(git_repo).ok_or_else(|| anyhow!("No remote configured"))?;
1262-
1263-
// Ensure trunk exists in tree
1264-
state.ensure_trunk(git_repo, repo);
1265-
1266-
if import_all {
1267-
// Import all open PRs
1268-
import_all_prs(git_repo, &client, &repo_id, state, repo, &trunk.main_branch)?;
1269-
} else {
1270-
// Import just this branch's chain
1271-
import_branch_chain(
1272-
git_repo,
1273-
&client,
1274-
&repo_id,
1275-
state,
1276-
repo,
1277-
branch,
1278-
&trunk.main_branch,
1279-
)?;
1280-
}
1281-
1282-
state.save_state()?;
1283-
println!("\n{}", "Import complete!".green().bold());
1284-
1285-
// Show the tree
1286-
println!();
1287-
let Some(tree) = state.get_tree(repo) else {
1288-
return Ok(());
1289-
};
1290-
let pr_cache = fetch_pr_cache(git_repo);
1291-
let display_authors = github::load_display_authors();
1292-
let renderable = render::compute_renderable_tree(
1293-
git_repo,
1294-
tree,
1295-
branch,
1296-
false,
1297-
pr_cache.as_ref(),
1298-
&display_authors,
1299-
);
1300-
render::render_cli(&renderable, false);
1301-
1302-
Ok(())
1303-
}
1304-
1305-
/// Import a single branch's PR chain back to trunk
1306-
fn import_branch_chain(
1307-
git_repo: &GitRepo,
1308-
client: &github::GitHubClient,
1309-
repo_id: &github::RepoIdentifier,
1310-
state: &mut State,
1311-
repo: &str,
1312-
branch: &str,
1313-
trunk: &str,
1314-
) -> Result<()> {
1315-
// Find PR for this branch
1316-
let pr = client.find_pr_for_branch(repo_id, branch)?.ok_or_else(|| {
1317-
anyhow!(
1318-
"No open PR found for branch '{}'. Nothing to import.",
1319-
branch
1320-
)
1321-
})?;
1322-
1323-
println!(
1324-
"Found PR #{} for '{}' (base: '{}')",
1325-
pr.number.to_string().green(),
1326-
branch.yellow(),
1327-
pr.base.ref_name.cyan()
1328-
);
1329-
1330-
// Build the chain from this branch up to trunk
1331-
let mut chain: Vec<(String, Option<github::PullRequest>)> =
1332-
vec![(branch.to_string(), Some(pr.clone()))];
1333-
let mut current_base = pr.base.ref_name.clone();
1334-
1335-
while current_base != trunk {
1336-
// Check if we already have this branch in our chain (cycle detection)
1337-
if chain.iter().any(|(b, _)| b == &current_base) {
1338-
println!(
1339-
"{} Detected cycle at '{}', stopping chain walk",
1340-
"Warning:".yellow().bold(),
1341-
current_base
1342-
);
1343-
break;
1344-
}
1345-
1346-
// Find PR for the base branch
1347-
match client.find_pr_for_branch(repo_id, &current_base)? {
1348-
Some(base_pr) => {
1349-
println!(
1350-
"Found PR #{} for '{}' (base: '{}')",
1351-
base_pr.number.to_string().green(),
1352-
current_base.yellow(),
1353-
base_pr.base.ref_name.cyan()
1354-
);
1355-
let next_base = base_pr.base.ref_name.clone();
1356-
chain.push((current_base.clone(), Some(base_pr)));
1357-
current_base = next_base;
1358-
}
1359-
None => {
1360-
// No PR for this base - it might be an intermediate branch or already merged
1361-
// Check if it's the trunk
1362-
if current_base == trunk {
1363-
break;
1364-
}
1365-
println!(
1366-
"{} No PR found for '{}', assuming it's an intermediate branch",
1367-
"Note:".cyan().bold(),
1368-
current_base.yellow()
1369-
);
1370-
chain.push((current_base.clone(), None));
1371-
// We can't walk further without a PR, assume parent is trunk
1372-
break;
1373-
}
1374-
}
1375-
}
1376-
1377-
// Reverse to process from trunk-side down
1378-
chain.reverse();
1379-
1380-
// Mount each branch in order
1381-
let mut parent = trunk.to_string();
1382-
for (branch_name, pr_opt) in chain {
1383-
// Check if branch exists locally
1384-
if !git_repo.branch_exists(&branch_name) {
1385-
println!(
1386-
"{} Branch '{}' doesn't exist locally, skipping",
1387-
"Note:".cyan().bold(),
1388-
branch_name.yellow()
1389-
);
1390-
continue;
1391-
}
1392-
1393-
// Mount this branch under the parent
1394-
if !state.branch_exists_in_tree(repo, &branch_name) {
1395-
println!(
1396-
"Mounting '{}' on '{}'",
1397-
branch_name.yellow(),
1398-
parent.green()
1399-
);
1400-
state.mount(git_repo, repo, &branch_name, Some(parent.clone()))?;
1401-
} else {
1402-
println!("'{}' already in tree", branch_name.yellow());
1403-
}
1404-
1405-
// Store PR number if we have one
1406-
if let Some(pr) = pr_opt
1407-
&& let Some(tree_branch) =
1408-
find_branch_by_name_mut(state.get_tree_mut(repo).unwrap(), &branch_name)
1409-
&& tree_branch.pr_number.is_none()
1410-
{
1411-
tree_branch.pr_number = Some(pr.number);
1412-
}
1413-
1414-
parent = branch_name;
1415-
}
1416-
1417-
Ok(())
1418-
}
1419-
1420-
/// Import all open PRs for the repo
1421-
fn import_all_prs(
1422-
git_repo: &GitRepo,
1423-
client: &github::GitHubClient,
1424-
repo_id: &github::RepoIdentifier,
1425-
state: &mut State,
1426-
repo: &str,
1427-
trunk: &str,
1428-
) -> Result<()> {
1429-
let all_prs = client.list_open_prs(repo_id, None)?.prs;
1430-
1431-
if all_prs.is_empty() {
1432-
println!("No open PRs found for this repository.");
1433-
return Ok(());
1434-
}
1435-
1436-
println!("Found {} open PR(s)", all_prs.len().to_string().green());
1437-
1438-
// Build a map of branch -> (parent, pr_number)
1439-
let mut branch_parents: std::collections::HashMap<String, (String, u64)> =
1440-
std::collections::HashMap::new();
1441-
1442-
for (branch_name, pr) in &all_prs {
1443-
branch_parents.insert(branch_name.clone(), (pr.base.ref_name.clone(), pr.number));
1444-
}
1445-
1446-
// Find branches whose parent is trunk or another PR branch (roots of stacks)
1447-
// Then build each stack
1448-
let mut imported = std::collections::HashSet::new();
1449-
1450-
// Process in order: first branches based on trunk, then their children, etc.
1451-
let mut to_process: Vec<String> = branch_parents
1452-
.iter()
1453-
.filter(|(_, (parent, _))| parent == trunk)
1454-
.map(|(branch, _)| branch.clone())
1455-
.collect();
1456-
1457-
while !to_process.is_empty() {
1458-
let branch_name = to_process.remove(0);
1459-
1460-
if imported.contains(&branch_name) {
1461-
continue;
1462-
}
1463-
1464-
// Get parent and PR number
1465-
let (parent, pr_number) = match branch_parents.get(&branch_name) {
1466-
Some((p, n)) => (p.clone(), *n),
1467-
None => continue,
1468-
};
1469-
1470-
// Determine actual parent (could be trunk or another imported branch)
1471-
let actual_parent = if parent == trunk || imported.contains(&parent) {
1472-
parent.clone()
1473-
} else {
1474-
// Parent not yet imported - skip for now, will process later
1475-
to_process.push(branch_name);
1476-
continue;
1477-
};
1478-
1479-
// Check if branch exists locally
1480-
if !git_repo.branch_exists(&branch_name) {
1481-
println!(
1482-
"{} Branch '{}' doesn't exist locally (PR #{}), skipping",
1483-
"Note:".cyan().bold(),
1484-
branch_name.yellow(),
1485-
pr_number
1486-
);
1487-
imported.insert(branch_name.clone());
1488-
continue;
1489-
}
1490-
1491-
// Mount this branch
1492-
if !state.branch_exists_in_tree(repo, &branch_name) {
1493-
println!(
1494-
"Mounting '{}' on '{}' (PR #{})",
1495-
branch_name.yellow(),
1496-
actual_parent.green(),
1497-
pr_number
1498-
);
1499-
state.mount(git_repo, repo, &branch_name, Some(actual_parent.clone()))?;
1500-
}
1501-
1502-
// Store PR number
1503-
if let Some(tree_branch) =
1504-
find_branch_by_name_mut(state.get_tree_mut(repo).unwrap(), &branch_name)
1505-
&& tree_branch.pr_number.is_none()
1506-
{
1507-
tree_branch.pr_number = Some(pr_number);
1508-
}
1509-
1510-
imported.insert(branch_name.clone());
1511-
1512-
// Add children of this branch to process list
1513-
for (child, (child_parent, _)) in &branch_parents {
1514-
if child_parent == &branch_name && !imported.contains(child) {
1515-
to_process.push(child.clone());
1516-
}
1517-
}
1518-
}
1519-
1520-
Ok(())
1521-
}
1522-
15231222
// ============== GitHub PR Commands ==============
15241223

15251224
fn handle_pr_command(

0 commit comments

Comments
 (0)