# End to End: From Issue to Merged Pull Request
Pick up a GitHub issue, let Gemini CLI create a branch, implement and test the change, open a pull request, review the diff yourself, and merge: that is the full professional loop, and Gemini fits inside it without you giving up control of any of the gates.
You already know Gemini CLI as an agentic coding tool that runs in your terminal. This lesson wires it into a real GitHub process. The point is not "let the AI write code." The point is treating Gemini CLI as a fast, tireless contributor whose work still passes through the same review discipline you would apply to any junior engineer.
A mature GitHub workflow looks like this:
1. An issue describes a problem or feature.
2. Someone branches off main.
3. They implement a small, focused change with tests.
4. They open a pull request (PR).
5. A human reviews the diff.
6. CI passes, then someone merges.
Gemini CLI can drive steps 2 through 4 for you. Steps 5 and 6 stay human. That separation is the whole design: the model produces a proposal, and the PR is the artifact you inspect before anything touches main.
Gemini CLI can shell out to
gitghgh auth status # confirm you are logged in to GitHub
gh auth setup-git # let gh act as git's credential helper
gemini --version # confirm Gemini CLI is on your PATHThe key move is giving the CLI a project brief so it does not guess your conventions. Gemini CLI reads a GEMINI.md file at the repo root as persistent context for every session. Treat it like a contract:
# Project: billing-service
## Workflow rules
- Branch from `main` using `fix/<issue-number>-<slug>` or `feat/<issue-number>-<slug>`.
- One logical change per branch. Keep diffs small.
- Every code change needs a matching test.
- Run `pytest -q` before proposing a PR. Do not open a PR if tests fail.
## Conventions
- Python 3.12, formatted with `ruff format`.
- Conventional Commits for commit messages (`fix:`, `feat:`, `chore:`).
- Never touch `migrations/` without an explicit instruction.That last line matters. GEMINI.md is where you encode guardrails, not just style. Anything you would tell a new hire in their first week belongs here.
Say issue #212 reads: *"Refund endpoint returns 200 even when the payment ID is unknown; it should return 404."*
Start a session and hand the issue to the model by number. Gemini CLI can call gh issue view itself, so you do not need to paste the text:
gemini
> Read issue #212 with `gh issue view 212`, then summarize the bug and
the acceptance criteria before writing any code.Forcing a summary first is a deliberate technique. You want the model to restate the problem in its own words so you can catch a misread before it writes a line. If the summary is wrong, correct it now. This costs you ten seconds and saves you a bad diff.
Once the summary is right, let the CLI branch. Because GEMINI.md defined the naming rule, you can be terse:
> Create the working branch for this issue following the rules in GEMINI.md.Gemini CLI will run something like git checkout -b fix/212-refund-unknown-payment-404. Branch hygiene means one branch maps to one issue and one logical change. This is not bureaucracy. It is what keeps the eventual diff small enough for a human to actually read, and it lets you abandon a bad attempt with a single git branch -D instead of untangling mixed changes.
If the CLI ever tries to work directly on main, stop it. Working on main is the single most common way an agent quietly corrupts a repo.
Now the real work. Gemini CLI is agentic: it will read files, propose edits, and ask to run commands. Approve those actions deliberately.
> Fix issue #212. Locate the refund handler, return 404 when the payment
ID is not found, and add a test that fails on the old behavior.
Run `pytest -q` when done and show me the result.Two things to insist on here:
Tests are part of the change, not a follow-up. A change without a test is a change nobody can trust to stay fixed. The instruction above asks for a test that *fails on the old behavior*, which is how you prove the test is real and not a rubber stamp.
The model runs the tests, and you read the output. Do not accept "I added the fix" on faith. If pytest is red, the loop is not done. Send the failure back:
> pytest failed with `KeyError: 'payment_id'` in test_refund.py:41.
Fix the test setup, do not change the assertion.That last clause protects you from a classic failure mode: the model "fixing" a red test by weakening the assertion until it passes. You are the one who decides what the test should prove.
Before any PR exists, look at the raw diff yourself:
> Show me `git diff --stat`, then the full diff.--stat first gives you the shape of the change: which files, how many lines. If the model touched twelve files to fix a 404, something is wrong. A tight, on-topic diff is a signal the model understood the task. A sprawling one is a signal to reset.
When the diff is clean, let the CLI commit and open the PR:
> Commit with a Conventional Commit message, push the branch, and open a
PR against `main` with `gh pr create`. In the PR body, link issue #212
with "Closes #212" and summarize what changed and how you tested it.The Closes #212 keyword is not cosmetic. GitHub auto-closes the linked issue when the PR merges, keeping your issue tracker honest. A good PR body written by the model gives your human reviewer (or your future self) the context to review fast.
Here is the part that does not get automated, on purpose. The PR is now on GitHub. Open it in the browser and review the diff as if a stranger wrote it, because functionally one did.
Ask the concrete questions:
This is also where a second AI reviewer earns its place. Gemini Code Assist can review pull requests on GitHub, leaving inline comments on the diff. Installed on the repo, it comments automatically when a PR opens. Treat that as a first pass that surfaces obvious issues (missing edge cases, style drift), not as the approval itself. The human still clicks Approve.
The reason to keep the gate human is accountability. When this refund code ships and something breaks at 2 a.m., "the model approved it" is not an answer anyone accepts. The person who merged owns the change. Gemini CLI drafted it; you shipped it.
Vérification des acquis
1. In the automated issue-to-merge loop described, which steps remain strictly under human control?
2. What is the primary purpose of a GEMINI.md file at the repo root?
3. The lesson frames Gemini CLI's role in the workflow using which analogy?
4. Select ALL correct answers about why the pull request is central to this workflow.
Sélectionnez toutes les réponses correctes.
5. Select ALL correct answers about the setup Gemini CLI relies on to participate in the GitHub process.
Sélectionnez toutes les réponses correctes.
Your branch-protection rules on main should require green CI before merge. That is standard GitHub configuration and it applies to AI-authored PRs exactly as it does to human ones. Nothing merges red.
If CI fails on something local tests missed (a linter, an integration test, a type check), feed the failure back into the same CLI session:
> The `ruff check` step failed in CI on unused import in refund.py.
Fix it and push to the same branch.The PR updates in place, CI re-runs. Once it is green and you have approved, merge:
> Merge the PR with `gh pr merge --squash --delete-branch`.--squash collapses the model's intermediate commits into one clean commit on main, so your history reads as one change per issue rather than a trail of "fix test" commits. --delete-branch closes the loop on branch hygiene: the throwaway branch is gone, and Closes #212 retires the issue.
Every discipline in this lesson (one issue per branch, a test with every change, reviewing --stat before the full diff, squash-merging) points at the same target: keeping the unit of change small enough that a human can fully understand it in a few minutes.
This matters more with an AI contributor, not less. Gemini CLI can generate a 600-line change as easily as a 20-line one, and a long context windowcontext windowThe context window is the maximum amount of text (measured in tokens) a language model can process at once, including both the input prompt and the generated output.Voir la définition complète → means it will happily attempt sprawling refactors. The bottleneck is not the model's ability to write code. It is your ability to verify it. Small diffs keep verification cheap, which keeps the loop fast and safe.
When you feel the urge to say "also refactor the whole payments module while you're in there," resist it. That is a second issue and a second branch.
Once this loop is muscle memory, the same shape scales up:
The interactive loop is the foundation. Automate outward from it only once you trust the gate.
main.git diff --stat first, squash-merge, delete the branch. Small units of change are the thing that makes an AI contributor safe to work with.