panqake 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ **/*.pyc
panqake-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: panqake
3
+ Version: 0.1.0
4
+ Summary: Git Branch Stacking Utility
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: prompt-toolkit>=3.0.51
7
+ Requires-Dist: questionary>=2.1.0
8
+ Description-Content-Type: text/markdown
9
+
10
+ # Panqake - Git Branch Stacking Utility
11
+
12
+ Panqake is a set of shell utilities for implementing the git-stacking workflow. It helps manage stacked branches, making it easier to work with multiple dependent pull requests.
13
+
14
+ ## Installation
15
+
16
+ 1. Clone this repository:
17
+
18
+ ```bash
19
+ git clone https://github.com/yourusername/panqake.git
20
+ ```
21
+
22
+ 2. Add the panqake directory to your PATH or create symlinks to the panqake script in a directory that's already in your PATH.
23
+
24
+ 3. Dependencies:
25
+ - jq: For JSON processing
26
+ - gh: GitHub CLI (optional, only needed for PR creation)
27
+
28
+ ## Usage
29
+
30
+ ### Create a new branch in the stack
31
+
32
+ ```bash
33
+ panqake new feature-login
34
+ ```
35
+
36
+ This creates a new branch based on your current branch and tracks the relationship.
37
+
38
+ ### View the branch stack
39
+
40
+ ```bash
41
+ panqake list
42
+ ```
43
+
44
+ Displays a tree view of your current branch stack.
45
+
46
+ ### Update branches after changes
47
+
48
+ ```bash
49
+ panqake update
50
+ ```
51
+
52
+ After making changes to a branch, this command rebases all child branches to incorporate your changes.
53
+
54
+ ### Delete a branch and relink the stack
55
+
56
+ ```bash
57
+ panqake delete feature-old
58
+ ```
59
+
60
+ Deletes a branch and relinks its children to its parent, maintaining the stack structure.
61
+
62
+ ### Create PRs for the branch stack
63
+
64
+ ```bash
65
+ panqake pr
66
+ ```
67
+
68
+ Creates pull requests for each branch in the stack, starting from the bottom.
69
+
70
+ ## Workflow Example
71
+
72
+ 1. Start a new feature stack from main:
73
+
74
+ ```bash
75
+ git checkout main
76
+ panqake new feature-base
77
+ ```
78
+
79
+ 2. Make your initial changes and commit.
80
+
81
+ 3. Create a dependent branch for additional work:
82
+
83
+ ```bash
84
+ panqake new feature-ui
85
+ ```
86
+
87
+ 4. Make changes and commit in the feature-ui branch.
88
+
89
+ 5. If you need to update the feature-base branch:
90
+
91
+ ```bash
92
+ git checkout feature-base
93
+ # Make changes and commit
94
+ panqake update
95
+ ```
96
+
97
+ 6. Create PRs for your stack:
98
+ ```bash
99
+ panqake pr
100
+ ```
101
+
102
+ ## License
103
+
104
+ MIT
105
+
106
+ ## Contributing
107
+
108
+ Contributions are welcome! Please feel free to submit a Pull Request.
109
+
110
+ ## Changelog
111
+
112
+ ### v0.1.1 (Unreleased)
113
+
114
+ - Switched CLI interface from prompt_toolkit to questionary for improved user experience
115
+ - Enhanced command-line prompts with better styling and autocomplete
116
+ - Fixed styling issues in branch listing display
117
+ - Improved output formatting for colored text
118
+ - Added custom color scheme that works well on both light and dark terminals
119
+ - Added documentation for the style system
120
+
121
+ ### v0.1.0
122
+
123
+ - Initial release with core functionality
124
+ - Branch stacking management
125
+ - PR creation support
@@ -0,0 +1,116 @@
1
+ # Panqake - Git Branch Stacking Utility
2
+
3
+ Panqake is a set of shell utilities for implementing the git-stacking workflow. It helps manage stacked branches, making it easier to work with multiple dependent pull requests.
4
+
5
+ ## Installation
6
+
7
+ 1. Clone this repository:
8
+
9
+ ```bash
10
+ git clone https://github.com/yourusername/panqake.git
11
+ ```
12
+
13
+ 2. Add the panqake directory to your PATH or create symlinks to the panqake script in a directory that's already in your PATH.
14
+
15
+ 3. Dependencies:
16
+ - jq: For JSON processing
17
+ - gh: GitHub CLI (optional, only needed for PR creation)
18
+
19
+ ## Usage
20
+
21
+ ### Create a new branch in the stack
22
+
23
+ ```bash
24
+ panqake new feature-login
25
+ ```
26
+
27
+ This creates a new branch based on your current branch and tracks the relationship.
28
+
29
+ ### View the branch stack
30
+
31
+ ```bash
32
+ panqake list
33
+ ```
34
+
35
+ Displays a tree view of your current branch stack.
36
+
37
+ ### Update branches after changes
38
+
39
+ ```bash
40
+ panqake update
41
+ ```
42
+
43
+ After making changes to a branch, this command rebases all child branches to incorporate your changes.
44
+
45
+ ### Delete a branch and relink the stack
46
+
47
+ ```bash
48
+ panqake delete feature-old
49
+ ```
50
+
51
+ Deletes a branch and relinks its children to its parent, maintaining the stack structure.
52
+
53
+ ### Create PRs for the branch stack
54
+
55
+ ```bash
56
+ panqake pr
57
+ ```
58
+
59
+ Creates pull requests for each branch in the stack, starting from the bottom.
60
+
61
+ ## Workflow Example
62
+
63
+ 1. Start a new feature stack from main:
64
+
65
+ ```bash
66
+ git checkout main
67
+ panqake new feature-base
68
+ ```
69
+
70
+ 2. Make your initial changes and commit.
71
+
72
+ 3. Create a dependent branch for additional work:
73
+
74
+ ```bash
75
+ panqake new feature-ui
76
+ ```
77
+
78
+ 4. Make changes and commit in the feature-ui branch.
79
+
80
+ 5. If you need to update the feature-base branch:
81
+
82
+ ```bash
83
+ git checkout feature-base
84
+ # Make changes and commit
85
+ panqake update
86
+ ```
87
+
88
+ 6. Create PRs for your stack:
89
+ ```bash
90
+ panqake pr
91
+ ```
92
+
93
+ ## License
94
+
95
+ MIT
96
+
97
+ ## Contributing
98
+
99
+ Contributions are welcome! Please feel free to submit a Pull Request.
100
+
101
+ ## Changelog
102
+
103
+ ### v0.1.1 (Unreleased)
104
+
105
+ - Switched CLI interface from prompt_toolkit to questionary for improved user experience
106
+ - Enhanced command-line prompts with better styling and autocomplete
107
+ - Fixed styling issues in branch listing display
108
+ - Improved output formatting for colored text
109
+ - Added custom color scheme that works well on both light and dark terminals
110
+ - Added documentation for the style system
111
+
112
+ ### v0.1.0
113
+
114
+ - Initial release with core functionality
115
+ - Branch stacking management
116
+ - PR creation support
@@ -0,0 +1,33 @@
1
+ [project]
2
+ name = "panqake"
3
+ version = "0.1.0"
4
+ description = "Git Branch Stacking Utility"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "prompt-toolkit>=3.0.51",
9
+ "questionary>=2.1.0",
10
+ ]
11
+
12
+ [project.scripts]
13
+ panqake = "panqake.cli:main"
14
+ pq = "panqake.cli:main"
15
+
16
+ [build-system]
17
+ requires = ["hatchling"]
18
+ build-backend = "hatchling.build"
19
+
20
+ [tool.hatch.build.targets.wheel]
21
+ packages = ["src/panqake"]
22
+
23
+ [tool.hatch.build.targets.sdist]
24
+ include = [
25
+ "src/panqake",
26
+ "README.md",
27
+ ]
28
+
29
+ [dependency-groups]
30
+ dev = [
31
+ "pytest>=8.3.5",
32
+ "hatch",
33
+ ]
@@ -0,0 +1,3 @@
1
+ """Panqake - Git branch stacking utility."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,6 @@
1
+ """Allow running the package directly with python -m panqake."""
2
+
3
+ from panqake.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Panqake - Git Branch Stacking Utility
4
+ A Python implementation of git-stacking workflow management
5
+ """
6
+
7
+ import argparse
8
+ import sys
9
+
10
+ from panqake.commands.delete import delete_branch
11
+ from panqake.commands.list import list_branches
12
+ from panqake.commands.new import create_new_branch
13
+ from panqake.commands.pr import create_pull_requests
14
+ from panqake.commands.switch import switch_branch
15
+ from panqake.commands.update import update_branches
16
+ from panqake.utils.config import init_panqake
17
+ from panqake.utils.git import is_git_repo
18
+
19
+
20
+ def main():
21
+ """Main entry point for the panqake CLI."""
22
+ parser = argparse.ArgumentParser(
23
+ description="Panqake - Git Branch Stacking Utility"
24
+ )
25
+ subparsers = parser.add_subparsers(dest="command", help="Command to run")
26
+
27
+ # new command
28
+ new_parser = subparsers.add_parser("new", help="Create a new branch in the stack")
29
+ new_parser.add_argument(
30
+ "branch_name",
31
+ nargs="?",
32
+ help="Name of the new branch to create",
33
+ )
34
+ new_parser.add_argument(
35
+ "base_branch",
36
+ nargs="?",
37
+ help="Optional base branch (defaults to current branch)",
38
+ )
39
+
40
+ # list command
41
+ list_parser = subparsers.add_parser("list", help="List the branch stack")
42
+ list_parser.add_argument(
43
+ "branch_name",
44
+ nargs="?",
45
+ help="Optional branch to start from (defaults to current branch)",
46
+ )
47
+
48
+ # update command
49
+ update_parser = subparsers.add_parser(
50
+ "update", help="Update branches after changes"
51
+ )
52
+ update_parser.add_argument(
53
+ "branch_name",
54
+ nargs="?",
55
+ help="Optional branch to start from (defaults to current branch)",
56
+ )
57
+
58
+ # delete command
59
+ delete_parser = subparsers.add_parser(
60
+ "delete", help="Delete a branch and relink the stack"
61
+ )
62
+ delete_parser.add_argument("branch_name", help="Name of the branch to delete")
63
+
64
+ # pr command
65
+ pr_parser = subparsers.add_parser("pr", help="Create PRs for the branch stack")
66
+ pr_parser.add_argument(
67
+ "branch_name",
68
+ nargs="?",
69
+ help="Optional branch to start from (defaults to current branch)",
70
+ )
71
+
72
+ # switch command
73
+ switch_parser = subparsers.add_parser(
74
+ "switch", help="Interactively switch between branches"
75
+ )
76
+ switch_parser.add_argument(
77
+ "branch_name",
78
+ nargs="?",
79
+ help="Optional branch to switch to (defaults to interactive selection)",
80
+ )
81
+
82
+ args = parser.parse_args()
83
+
84
+ if not args.command:
85
+ parser.print_help()
86
+ return
87
+
88
+ # Initialize panqake directory and files
89
+ init_panqake()
90
+
91
+ # Check if we're in a git repository
92
+ if not is_git_repo():
93
+ print("Error: Not in a git repository")
94
+ sys.exit(1)
95
+
96
+ # Execute the appropriate command
97
+ if args.command == "new":
98
+ create_new_branch(args.branch_name, args.base_branch)
99
+ elif args.command == "list":
100
+ list_branches(args.branch_name)
101
+ elif args.command == "update":
102
+ update_branches(args.branch_name)
103
+ elif args.command == "delete":
104
+ delete_branch(args.branch_name)
105
+ elif args.command == "pr":
106
+ create_pull_requests(args.branch_name)
107
+ elif args.command == "switch":
108
+ switch_branch(args.branch_name)
109
+
110
+
111
+ if __name__ == "__main__":
112
+ main()
@@ -0,0 +1 @@
1
+ """Command modules for panqake git-stacking utility."""
@@ -0,0 +1,153 @@
1
+ """Command for deleting a branch and relinking the stack."""
2
+
3
+ import sys
4
+
5
+ from panqake.utils.config import (
6
+ add_to_stack,
7
+ get_child_branches,
8
+ get_parent_branch,
9
+ remove_from_stack,
10
+ )
11
+ from panqake.utils.git import branch_exists, get_current_branch, run_git_command
12
+ from panqake.utils.questionary_prompt import (
13
+ format_branch,
14
+ print_formatted_text,
15
+ prompt_confirm,
16
+ )
17
+
18
+
19
+ def validate_branch_for_deletion(branch_name):
20
+ """Validate that a branch can be deleted."""
21
+ current_branch = get_current_branch()
22
+
23
+ # Check if target branch exists
24
+ if not branch_exists(branch_name):
25
+ print_formatted_text(
26
+ f"<warning>Error: Branch '{branch_name}' does not exist</warning>"
27
+ )
28
+ sys.exit(1)
29
+
30
+ # Check if target branch is the current branch
31
+ if branch_name == current_branch:
32
+ print_formatted_text(
33
+ "<warning>Error: Cannot delete the current branch. Please checkout another branch first.</warning>"
34
+ )
35
+ sys.exit(1)
36
+
37
+ return current_branch
38
+
39
+
40
+ def get_branch_relationships(branch_name):
41
+ """Get parent and child branches and validate parent exists."""
42
+ parent_branch = get_parent_branch(branch_name)
43
+ child_branches = get_child_branches(branch_name)
44
+
45
+ # Ensure parent branch exists
46
+ if parent_branch and not branch_exists(parent_branch):
47
+ print_formatted_text(
48
+ f"<warning>Error: Parent branch '{parent_branch}' does not exist</warning>"
49
+ )
50
+ sys.exit(1)
51
+
52
+ return parent_branch, child_branches
53
+
54
+
55
+ def display_deletion_info(branch_name, parent_branch, child_branches):
56
+ """Display deletion information and ask for confirmation."""
57
+ print_formatted_text(
58
+ f"<info>Branch to delete:</info> {format_branch(branch_name, danger=True)}"
59
+ )
60
+ if parent_branch:
61
+ print_formatted_text(
62
+ f"<info>Parent branch:</info> {format_branch(parent_branch)}"
63
+ )
64
+ if child_branches:
65
+ print_formatted_text("<info>Child branches that will be relinked:</info>")
66
+ for child in child_branches:
67
+ print_formatted_text(f" {format_branch(child)}")
68
+
69
+ # Confirm deletion
70
+ if not prompt_confirm("Are you sure you want to delete this branch?"):
71
+ print_formatted_text("<info>Branch deletion cancelled.</info>")
72
+ return False
73
+
74
+ return True
75
+
76
+
77
+ def relink_child_branches(child_branches, parent_branch, current_branch, branch_name):
78
+ """Relink child branches to the parent branch."""
79
+ if not child_branches:
80
+ return True
81
+
82
+ print_formatted_text(
83
+ f"<info>Relinking child branches to parent '{parent_branch}'...</info>"
84
+ )
85
+
86
+ for child in child_branches:
87
+ print_formatted_text(
88
+ f"<info>Processing child branch:</info> {format_branch(child)}"
89
+ )
90
+
91
+ # Checkout the child branch
92
+ checkout_result = run_git_command(["checkout", child])
93
+ if checkout_result is None:
94
+ print_formatted_text(
95
+ f"<warning>Error: Failed to checkout branch '{child}'</warning>"
96
+ )
97
+ run_git_command(["checkout", current_branch])
98
+ sys.exit(1)
99
+
100
+ # Rebase onto the grandparent branch
101
+ if parent_branch:
102
+ rebase_result = run_git_command(["rebase", parent_branch])
103
+ if rebase_result is None:
104
+ print_formatted_text(
105
+ f"<warning>Error: Rebase conflict detected in branch '{child}'</warning>"
106
+ )
107
+ print_formatted_text(
108
+ "<warning>Please resolve conflicts and run 'git rebase --continue'</warning>"
109
+ )
110
+ print_formatted_text(
111
+ f"<warning>Then run 'panqake delete {branch_name}' again to retry</warning>"
112
+ )
113
+ sys.exit(1)
114
+
115
+ # Update stack metadata
116
+ add_to_stack(child, parent_branch)
117
+
118
+ return True
119
+
120
+
121
+ def delete_branch(branch_name):
122
+ """Delete a branch and relink the stack."""
123
+ current_branch = validate_branch_for_deletion(branch_name)
124
+ parent_branch, child_branches = get_branch_relationships(branch_name)
125
+
126
+ if not display_deletion_info(branch_name, parent_branch, child_branches):
127
+ return
128
+
129
+ print_formatted_text(
130
+ f"<info>Deleting branch '{branch_name}' from the stack...</info>"
131
+ )
132
+
133
+ # Process child branches
134
+ relink_child_branches(child_branches, parent_branch, current_branch, branch_name)
135
+
136
+ # Return to original branch if it's not the one being deleted
137
+ if branch_name != current_branch:
138
+ run_git_command(["checkout", current_branch])
139
+
140
+ # Delete the branch
141
+ delete_result = run_git_command(["branch", "-D", branch_name])
142
+ if delete_result is None:
143
+ print_formatted_text(
144
+ f"<warning>Error: Failed to delete branch '{branch_name}'</warning>"
145
+ )
146
+ sys.exit(1)
147
+
148
+ # Remove from stack metadata
149
+ remove_from_stack(branch_name)
150
+
151
+ print_formatted_text(
152
+ f"<success>Success! Deleted branch '{branch_name}' and relinked the stack</success>"
153
+ )
@@ -0,0 +1,74 @@
1
+ """Command for listing branches in the stack."""
2
+
3
+ import sys
4
+
5
+ from panqake.utils.config import get_child_branches, get_parent_branch
6
+ from panqake.utils.git import branch_exists, get_current_branch
7
+
8
+
9
+ def find_stack_root(branch):
10
+ """Find the root of the stack for a given branch."""
11
+ parent = get_parent_branch(branch)
12
+
13
+ if not parent:
14
+ return branch
15
+ else:
16
+ return find_stack_root(parent)
17
+
18
+
19
+ def print_branch_tree(branch, indent="", is_last_sibling=True):
20
+ """Recursively print the branch tree."""
21
+ current_branch = get_current_branch()
22
+ is_current = branch == current_branch
23
+
24
+ # Determine the connector for the current branch
25
+ if indent: # Not the root
26
+ connector = "└── " if is_last_sibling else "├── "
27
+ else: # Root branch
28
+ connector = ""
29
+
30
+ # Format and print the current branch line
31
+ prefix = f"{indent}{connector}"
32
+ if is_current:
33
+ # Apply branch styling using ANSI codes
34
+ branch_display = f"* {branch}"
35
+ # Print prefix first, then styled branch with ANSI codes
36
+ print(f"{prefix}\033[92m{branch_display}\033[0m")
37
+ else:
38
+ # Non-current branches use default terminal text color
39
+ print(f"{prefix}{branch}")
40
+
41
+ # Prepare the indentation for children
42
+ # Add a vertical bar if this branch is not the last sibling, otherwise add spaces
43
+ child_indent = indent + (" " if is_last_sibling else "│ ")
44
+
45
+ # Get children of this branch
46
+ children = get_child_branches(branch)
47
+ num_children = len(children)
48
+
49
+ if children:
50
+ for i, child in enumerate(children):
51
+ is_last_child = i == num_children - 1
52
+ print_branch_tree(child, child_indent, is_last_child)
53
+
54
+
55
+ def list_branches(branch_name=None):
56
+ """List the branch stack."""
57
+ # If no branch specified, use current branch
58
+ if not branch_name:
59
+ branch_name = get_current_branch()
60
+
61
+ # Check if target branch exists
62
+ if not branch_exists(branch_name):
63
+ # Use standard print with ANSI codes for warning style (yellow)
64
+ print(f"\033[93mError: Branch '{branch_name}' does not exist\033[0m")
65
+ sys.exit(1)
66
+
67
+ # Find the root of the stack for the target branch
68
+ root_branch = find_stack_root(branch_name)
69
+
70
+ # Use standard print with ANSI codes for info style (cyan)
71
+ print(f"\033[96mBranch stack (current: {get_current_branch()})\033[0m")
72
+
73
+ # Initial call starts with no indent and assumes the root is the 'last sibling' conceptually
74
+ print_branch_tree(root_branch, indent="", is_last_sibling=True)
@@ -0,0 +1,56 @@
1
+ """Command for creating a new branch in the stack."""
2
+
3
+ import sys
4
+
5
+ from panqake.utils.config import add_to_stack
6
+ from panqake.utils.git import (
7
+ branch_exists,
8
+ get_current_branch,
9
+ list_all_branches,
10
+ run_git_command,
11
+ )
12
+ from panqake.utils.questionary_prompt import BranchNameValidator, prompt_input
13
+
14
+
15
+ def create_new_branch(branch_name=None, base_branch=None):
16
+ """Create a new branch in the stack."""
17
+ # If no branch name specified, prompt for it
18
+ if not branch_name:
19
+ validator = BranchNameValidator()
20
+ branch_name = prompt_input("Enter new branch name: ", validator=validator)
21
+
22
+ # If no base branch specified, use current branch but offer selection
23
+ current = get_current_branch()
24
+ if not base_branch:
25
+ base_branch = current
26
+ branches = list_all_branches()
27
+ if branches:
28
+ base_branch = prompt_input(
29
+ f"Enter base branch [default: {current}]: ",
30
+ completer=branches,
31
+ default=current,
32
+ )
33
+
34
+ # Check if the new branch already exists
35
+ if branch_exists(branch_name):
36
+ print(f"Error: Branch '{branch_name}' already exists")
37
+ sys.exit(1)
38
+
39
+ # Check if the base branch exists
40
+ if base_branch and not branch_exists(base_branch):
41
+ print(f"Error: Base branch '{base_branch}' does not exist")
42
+ sys.exit(1)
43
+
44
+ print(f"Creating new branch '{branch_name}' based on '{base_branch}'...")
45
+
46
+ # Create the new branch
47
+ result = run_git_command(["checkout", "-b", branch_name, base_branch])
48
+ if result is None:
49
+ print("Error: Failed to create new branch")
50
+ sys.exit(1)
51
+
52
+ # Record the dependency information
53
+ add_to_stack(branch_name, base_branch)
54
+
55
+ print(f"Success! Created new branch '{branch_name}' in the stack")
56
+ print(f"Parent branch: {base_branch}")