Skip to content

Generator

PR generator for the CodeMap Git module.

This class generates pull requests for git repositories.

logger module-attribute

logger = getLogger(__name__)

PRGenerator

Generator for Pull Requests.

This class handles generating pull request content (title and description) and creating/updating PRs on GitHub.

Source code in src/codemap/git/pr_generator/generator.py
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
class PRGenerator:
	"""
	Generator for Pull Requests.

	This class handles generating pull request content (title and
	description) and creating/updating PRs on GitHub.

	"""

	def __init__(
		self,
		repo_path: Path,
		llm_client: LLMClient,
	) -> None:
		"""
		Initialize the PR generator.

		Args:
		    repo_path: Path to the git repository
		    llm_client: LLMClient instance to use for content generation

		"""
		self.repo_path = repo_path
		self.client = llm_client

	def generate_content_from_commits(self, base_branch: str, head_branch: str, use_llm: bool = True) -> PRContent:
		"""
		Generate PR content (title and description) from commits.

		Args:
		    base_branch: Base branch (e.g., main)
		    head_branch: Head branch (e.g., feature-branch)
		    use_llm: Whether to use LLM for generation

		Returns:
		    Dictionary with 'title' and 'description' keys

		"""
		# Get commit messages between branches
		pgu = PRGitUtils.get_instance()
		commits = pgu.get_commit_messages(base_branch, head_branch)

		if not commits:
			return {"title": "Update branch", "description": "No changes in this PR."}

		if use_llm:
			# Generate title and description using LLM
			title = generate_pr_title_with_llm(commits, self.client)
			description = generate_pr_description_with_llm(commits, self.client)
		else:
			# Generate title and description using rule-based approach
			title = generate_pr_title_from_commits(commits)
			description = generate_pr_description_from_commits(commits)

		return {"title": title, "description": description}

	def generate_content_from_template(
		self, branch_name: str, description: str, workflow_strategy: str = "github-flow"
	) -> PRContent:
		"""
		Generate PR content (title and description) from a template.

		Args:
		    branch_name: Name of the branch
		    description: Short description of the changes
		    workflow_strategy: Git workflow strategy to use

		Returns:
		    Dictionary with 'title' and 'description' keys

		"""
		return generate_pr_content_from_template(branch_name, description, workflow_strategy)

	def suggest_branch_name(self, description: str, workflow_strategy: str = "github-flow") -> str:
		"""
		Suggest a branch name based on a description.

		Args:
		    description: Description of the branch
		    workflow_strategy: Git workflow strategy to use

		Returns:
		    Suggested branch name

		"""
		return suggest_branch_name(description, workflow_strategy)

	def create_pr(self, base_branch: str, head_branch: str, title: str, description: str) -> PullRequest:
		"""
		Create a pull request on GitHub.

		Args:
		    base_branch: Base branch (e.g., main)
		    head_branch: Head branch (e.g., feature-branch)
		    title: PR title
		    description: PR description

		Returns:
		    PullRequest object with PR details

		Raises:
		    GitError: If PR creation fails

		"""
		return create_pull_request(base_branch, head_branch, title, description)

	def update_pr(self, pr_number: int, title: str, description: str) -> PullRequest:
		"""
		Update an existing pull request.

		Args:
		    pr_number: PR number
		    title: New PR title
		    description: New PR description

		Returns:
		    Updated PullRequest object

		Raises:
		    GitError: If PR update fails

		"""
		return update_pull_request(pr_number, title, description)

	def get_existing_pr(self, branch_name: str) -> PullRequest | None:
		"""
		Get an existing PR for a branch.

		Args:
		    branch_name: Branch name

		Returns:
		    PullRequest object if found, None otherwise

		"""
		return get_existing_pr(branch_name)

	def create_or_update_pr(
		self,
		base_branch: str | None = None,
		head_branch: str | None = None,
		title: str | None = None,
		description: str | None = None,
		use_llm: bool = True,
		pr_number: int | None = None,
	) -> PullRequest:
		"""
		Create a new PR or update an existing one.

		Args:
		    base_branch: Base branch (defaults to default branch)
		    head_branch: Head branch
		    title: PR title (if None, will be generated)
		    description: PR description (if None, will be generated)
		    use_llm: Whether to use LLM for content generation
		    pr_number: PR number for update (if None, will create new PR)

		Returns:
		    PullRequest object

		Raises:
		    GitError: If PR creation/update fails

		"""
		# Get default branch if base_branch is not specified
		if base_branch is None:
			base_branch = get_default_branch()

		# Set default head_branch to current branch if not specified
		pgu = PRGitUtils.get_instance()
		if head_branch is None:
			try:
				head_branch = pgu.get_current_branch()
			except GitError as err:
				msg = "Failed to determine current branch"
				raise GitError(msg) from err

		# Check if PR exists
		existing_pr = None
		if pr_number is not None:
			# Updating an existing PR by number
			if title is None or description is None:
				# Need to fetch the PR to get current title/description
				existing_pr = self.get_existing_pr(head_branch)
				if existing_pr is None:
					msg = f"No PR found for branch {head_branch} with number {pr_number}"
					raise GitError(msg)

		else:
			# Look for existing PR for this branch
			existing_pr = self.get_existing_pr(head_branch)
			if existing_pr is not None:
				pr_number = existing_pr.number

		# Generate content if not provided
		if title is None or description is None:
			content = self.generate_content_from_commits(base_branch, head_branch, use_llm)
			if title is None:
				title = content["title"]
			if description is None:
				description = content["description"]

		# Create or update PR
		if pr_number is not None:
			# Update existing PR
			return self.update_pr(pr_number, title, description)
		# Create new PR
		return self.create_pr(base_branch, head_branch, title, description)

__init__

__init__(repo_path: Path, llm_client: LLMClient) -> None

Initialize the PR generator.

Parameters:

Name Type Description Default
repo_path Path

Path to the git repository

required
llm_client LLMClient

LLMClient instance to use for content generation

required
Source code in src/codemap/git/pr_generator/generator.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def __init__(
	self,
	repo_path: Path,
	llm_client: LLMClient,
) -> None:
	"""
	Initialize the PR generator.

	Args:
	    repo_path: Path to the git repository
	    llm_client: LLMClient instance to use for content generation

	"""
	self.repo_path = repo_path
	self.client = llm_client

repo_path instance-attribute

repo_path = repo_path

client instance-attribute

client = llm_client

generate_content_from_commits

generate_content_from_commits(
	base_branch: str, head_branch: str, use_llm: bool = True
) -> PRContent

Generate PR content (title and description) from commits.

Parameters:

Name Type Description Default
base_branch str

Base branch (e.g., main)

required
head_branch str

Head branch (e.g., feature-branch)

required
use_llm bool

Whether to use LLM for generation

True

Returns:

Type Description
PRContent

Dictionary with 'title' and 'description' keys

Source code in src/codemap/git/pr_generator/generator.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def generate_content_from_commits(self, base_branch: str, head_branch: str, use_llm: bool = True) -> PRContent:
	"""
	Generate PR content (title and description) from commits.

	Args:
	    base_branch: Base branch (e.g., main)
	    head_branch: Head branch (e.g., feature-branch)
	    use_llm: Whether to use LLM for generation

	Returns:
	    Dictionary with 'title' and 'description' keys

	"""
	# Get commit messages between branches
	pgu = PRGitUtils.get_instance()
	commits = pgu.get_commit_messages(base_branch, head_branch)

	if not commits:
		return {"title": "Update branch", "description": "No changes in this PR."}

	if use_llm:
		# Generate title and description using LLM
		title = generate_pr_title_with_llm(commits, self.client)
		description = generate_pr_description_with_llm(commits, self.client)
	else:
		# Generate title and description using rule-based approach
		title = generate_pr_title_from_commits(commits)
		description = generate_pr_description_from_commits(commits)

	return {"title": title, "description": description}

generate_content_from_template

generate_content_from_template(
	branch_name: str,
	description: str,
	workflow_strategy: str = "github-flow",
) -> PRContent

Generate PR content (title and description) from a template.

Parameters:

Name Type Description Default
branch_name str

Name of the branch

required
description str

Short description of the changes

required
workflow_strategy str

Git workflow strategy to use

'github-flow'

Returns:

Type Description
PRContent

Dictionary with 'title' and 'description' keys

Source code in src/codemap/git/pr_generator/generator.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def generate_content_from_template(
	self, branch_name: str, description: str, workflow_strategy: str = "github-flow"
) -> PRContent:
	"""
	Generate PR content (title and description) from a template.

	Args:
	    branch_name: Name of the branch
	    description: Short description of the changes
	    workflow_strategy: Git workflow strategy to use

	Returns:
	    Dictionary with 'title' and 'description' keys

	"""
	return generate_pr_content_from_template(branch_name, description, workflow_strategy)

suggest_branch_name

suggest_branch_name(
	description: str, workflow_strategy: str = "github-flow"
) -> str

Suggest a branch name based on a description.

Parameters:

Name Type Description Default
description str

Description of the branch

required
workflow_strategy str

Git workflow strategy to use

'github-flow'

Returns:

Type Description
str

Suggested branch name

Source code in src/codemap/git/pr_generator/generator.py
109
110
111
112
113
114
115
116
117
118
119
120
121
def suggest_branch_name(self, description: str, workflow_strategy: str = "github-flow") -> str:
	"""
	Suggest a branch name based on a description.

	Args:
	    description: Description of the branch
	    workflow_strategy: Git workflow strategy to use

	Returns:
	    Suggested branch name

	"""
	return suggest_branch_name(description, workflow_strategy)

create_pr

create_pr(
	base_branch: str,
	head_branch: str,
	title: str,
	description: str,
) -> PullRequest

Create a pull request on GitHub.

Parameters:

Name Type Description Default
base_branch str

Base branch (e.g., main)

required
head_branch str

Head branch (e.g., feature-branch)

required
title str

PR title

required
description str

PR description

required

Returns:

Type Description
PullRequest

PullRequest object with PR details

Raises:

Type Description
GitError

If PR creation fails

Source code in src/codemap/git/pr_generator/generator.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def create_pr(self, base_branch: str, head_branch: str, title: str, description: str) -> PullRequest:
	"""
	Create a pull request on GitHub.

	Args:
	    base_branch: Base branch (e.g., main)
	    head_branch: Head branch (e.g., feature-branch)
	    title: PR title
	    description: PR description

	Returns:
	    PullRequest object with PR details

	Raises:
	    GitError: If PR creation fails

	"""
	return create_pull_request(base_branch, head_branch, title, description)

update_pr

update_pr(
	pr_number: int, title: str, description: str
) -> PullRequest

Update an existing pull request.

Parameters:

Name Type Description Default
pr_number int

PR number

required
title str

New PR title

required
description str

New PR description

required

Returns:

Type Description
PullRequest

Updated PullRequest object

Raises:

Type Description
GitError

If PR update fails

Source code in src/codemap/git/pr_generator/generator.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def update_pr(self, pr_number: int, title: str, description: str) -> PullRequest:
	"""
	Update an existing pull request.

	Args:
	    pr_number: PR number
	    title: New PR title
	    description: New PR description

	Returns:
	    Updated PullRequest object

	Raises:
	    GitError: If PR update fails

	"""
	return update_pull_request(pr_number, title, description)

get_existing_pr

get_existing_pr(branch_name: str) -> PullRequest | None

Get an existing PR for a branch.

Parameters:

Name Type Description Default
branch_name str

Branch name

required

Returns:

Type Description
PullRequest | None

PullRequest object if found, None otherwise

Source code in src/codemap/git/pr_generator/generator.py
160
161
162
163
164
165
166
167
168
169
170
171
def get_existing_pr(self, branch_name: str) -> PullRequest | None:
	"""
	Get an existing PR for a branch.

	Args:
	    branch_name: Branch name

	Returns:
	    PullRequest object if found, None otherwise

	"""
	return get_existing_pr(branch_name)

create_or_update_pr

create_or_update_pr(
	base_branch: str | None = None,
	head_branch: str | None = None,
	title: str | None = None,
	description: str | None = None,
	use_llm: bool = True,
	pr_number: int | None = None,
) -> PullRequest

Create a new PR or update an existing one.

Parameters:

Name Type Description Default
base_branch str | None

Base branch (defaults to default branch)

None
head_branch str | None

Head branch

None
title str | None

PR title (if None, will be generated)

None
description str | None

PR description (if None, will be generated)

None
use_llm bool

Whether to use LLM for content generation

True
pr_number int | None

PR number for update (if None, will create new PR)

None

Returns:

Type Description
PullRequest

PullRequest object

Raises:

Type Description
GitError

If PR creation/update fails

Source code in src/codemap/git/pr_generator/generator.py
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
def create_or_update_pr(
	self,
	base_branch: str | None = None,
	head_branch: str | None = None,
	title: str | None = None,
	description: str | None = None,
	use_llm: bool = True,
	pr_number: int | None = None,
) -> PullRequest:
	"""
	Create a new PR or update an existing one.

	Args:
	    base_branch: Base branch (defaults to default branch)
	    head_branch: Head branch
	    title: PR title (if None, will be generated)
	    description: PR description (if None, will be generated)
	    use_llm: Whether to use LLM for content generation
	    pr_number: PR number for update (if None, will create new PR)

	Returns:
	    PullRequest object

	Raises:
	    GitError: If PR creation/update fails

	"""
	# Get default branch if base_branch is not specified
	if base_branch is None:
		base_branch = get_default_branch()

	# Set default head_branch to current branch if not specified
	pgu = PRGitUtils.get_instance()
	if head_branch is None:
		try:
			head_branch = pgu.get_current_branch()
		except GitError as err:
			msg = "Failed to determine current branch"
			raise GitError(msg) from err

	# Check if PR exists
	existing_pr = None
	if pr_number is not None:
		# Updating an existing PR by number
		if title is None or description is None:
			# Need to fetch the PR to get current title/description
			existing_pr = self.get_existing_pr(head_branch)
			if existing_pr is None:
				msg = f"No PR found for branch {head_branch} with number {pr_number}"
				raise GitError(msg)

	else:
		# Look for existing PR for this branch
		existing_pr = self.get_existing_pr(head_branch)
		if existing_pr is not None:
			pr_number = existing_pr.number

	# Generate content if not provided
	if title is None or description is None:
		content = self.generate_content_from_commits(base_branch, head_branch, use_llm)
		if title is None:
			title = content["title"]
		if description is None:
			description = content["description"]

	# Create or update PR
	if pr_number is not None:
		# Update existing PR
		return self.update_pr(pr_number, title, description)
	# Create new PR
	return self.create_pr(base_branch, head_branch, title, description)