OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 105

The GitHub contribution workflow

Move from issues and branches through PR review and checks to synchronization and cleanup after merging.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSGitHub repositories and collaboration ↗
About 14 min readStart reading

A reliable contribution does not end with “pushing the code.” It makes the issue, changes, review, CI results, and merge decision reproducible and understandable to others.

This article covers only the repository contribution process on GitHub: Issues, topic branches, forks, Pull Requests (PRs), reviews, required checks, and post-merge cleanup. For Git’s object model, branching basics, and remote synchronization details, refer to Git Branch Management and Working with Git Remotes; Git internals are not repeated here.

1. Choose a contribution model: shared repository or fork-and-pull#

GitHub Pull Requests do not prescribe a single way to organize contributions. Two models are common:

Model Local and remote relationships Who creates the topic branch Best suited to
Shared repository origin points to the shared repository; contributors have push access Contributors create and push branches in the shared repository Small teams and private projects within an organization
Fork-and-pull upstream points to the original repository; origin points to your fork Contributors create and push branches in their own forks Open-source projects and external contributors without write access to the original repository

Both models should respect the same boundary: do not develop directly on the default branch. A topic branch isolates changes so that CI and review can take place before merging.

1.1 Base, head, and the direction of a PR

A PR does not “combine two repositories.” It requests that commits from the head branch be merged into the base branch:

  • base: the target repository and branch, usually main in the maintainer’s repository.
  • head: the branch containing the changes for review; it can be a branch in the same repository or in a fork.
  • Confirm the base <- head direction shown on the PR page before creating it.

For example, when an external contributor submits a fix to the illustrative project, the direction is usually:

base: OWNER/example-theme-project:main
head: YOUR_GITHUB_LOGIN/example-theme-project:fix/123-compositor-note

Reversing the direction may treat main in the maintainer’s repository as the incoming changes, or send the PR to the wrong fork. Before submitting, check both the web comparison page and the gh pr create arguments.

1.2 Read the repository’s actual rules before contributing

Check the contribution guidance in this order:

  1. README: project goals, supported scope, how to run it, known limitations, and current requests.
  2. CONTRIBUTING.md or the development section of the documentation: naming, testing, commit, and review requirements.
  3. .github/ISSUE_TEMPLATE/ and .github/PULL_REQUEST_TEMPLATE*: requirements for forms or PR descriptions.
  4. CODEOWNERS: which files automatically trigger review requests, and from whom.
  5. Workflows and branch rules: required checks, permissions, and whether branches must be kept up to date.

If the repository lacks these files, do not invent an unwritten process. Clearly state your assumptions, validation environment, and unverified areas in the Issue or PR description.

1.3 Example: contributing desktop compatibility notes

Suppose a theme project welcomes compatibility reports for compositors that have not yet been tested, and its README asks for Fcitx5 and compositor versions plus reproducible symptoms. This is an illustrative scenario, with no corresponding personal repository, version snapshot, or test findings.

If the project has no CONTRIBUTING file, Issue/PR templates, or CODEOWNERS, contributors should rely on the README and verifiable facts in the PR, rather than assume an unwritten process. Testing one compositor does not establish that other desktops have been tested. For compatibility contributions like these, recording the environment, symptoms, and sanitized screenshots/logs in an Issue before deciding whether to submit code is usually easier to review than opening a large PR immediately.

2. From an Issue to a topic branch#

2.1 Turn the problem into a reviewable task first

An Issue or contribution plan should describe at least the current behavior and reproduction steps, expected behavior, scope of the changes, related Issues, and validation methods such as commands, versions, platforms, screenshots, and logs. Documentation, theme, and CI changes also need reproducible evidence; “it looks better” is no substitute for checking.

2.2 Shared repository: create a branch in the shared repository

The following assumes that the shared repository remote is named origin and its default branch is main:

git switch main
git fetch origin
git pull --ff-only origin main
git switch -c fix/123-compositor-note

# 编辑文件、运行项目规定的本地检查
git status --short
git diff --check
git add path/to/changed-file
git commit -m "docs: clarify compositor contribution status"
git push -u origin HEAD

git switch -c creates a topic branch from the current commit and switches to it. Before running it, make sure main has been synchronized to the base you intend to use. git push -u origin HEAD pushes the current branch to origin and sets up tracking of the branch with the same name, without requiring you to type the branch name again.

2.3 Fork-and-pull: distinguish origin from upstream

External contributors first fork the original repository, then clone their fork. Give the two remotes clear names from the outset:

git clone https://github.com/YOUR_GITHUB_LOGIN/REPOSITORY.git
cd REPOSITORY
git remote -v
git remote add upstream https://github.com/OWNER/REPOSITORY.git
git fetch upstream
git switch main
git merge --ff-only upstream/main
git push origin main
git switch -c fix/123-compositor-note

Here:

  • origin is your fork; push the topic branch here;
  • upstream is the original repository, used to fetch the maintainer’s latest base;
  • upstream/main is your local record of main in the original repository, not a credential or a special server.

If main in your fork has no unique commits, git merge --ff-only upstream/main brings it into alignment with a fast-forward; it refuses to integrate divergent histories. If your fork’s main has local commits, inspect whether they should be retained before choosing to rebase, merge, or recreate the fork. You can also synchronize a fork using Sync fork on the website, or:

gh repo sync YOUR_GITHUB_LOGIN/REPOSITORY -b main

This command synchronizes the specified main branch in the fork; the topic branch still needs to be updated separately.

2.4 When not to rebase

git rebase upstream/main is suitable for a personal topic branch that others do not yet depend on, or an unpublished local branch. It rewrites the hashes of the replayed commits, so do not proceed directly in the following situations:

  • Do not rebase a shared main, a protected branch, or a branch others are using.
  • If maintainers or other collaborators have been allowed to commit to the same PR’s head branch, communicate before rebasing.
  • If the project explicitly requires preserving branch merge history, do not switch to rebasing just to make the history “linear.”
  • Repeatedly rebasing solely to get around CI or review makes it difficult to trace the commits that comments refer to.

If you have confirmed that only you use the topic branch, you will usually need to update the remote safely after rebasing:

git fetch upstream
git switch fix/123-compositor-note
git rebase upstream/main

# 解决冲突后:git add <file> && git rebase --continue
git push --force-with-lease -u origin HEAD

--force-with-lease checks whether someone else has updated the remote branch since your last fetch. Do not treat git push --force as a routine synchronization command, and do not force-push to main.

2.5 Minimum checks before submitting

Before opening a PR, complete at least these checks:

git status --short
git diff --stat upstream/main...HEAD
git diff --check upstream/main...HEAD
git log --oneline --decorate upstream/main..HEAD

In a shared repository, you can replace upstream/main above with origin/main. The important point is to compare the current head with the PR’s actual base, rather than an outdated local branch. Then run the tests, builds, and formatting checks explicitly required by the README or repository CI.

3. Creating a Pull Request#

3.1 Creating one in the web interface

The web interface is useful for first-time contributions, uploading screenshots, or carefully checking base/head. Select the base and head repositories/branches first, then decide whether to mark the PR as Draft. State the outcome in the title and describe the problem, validation, risks, and unfinished work in the body. Finally, check Files changed, Checks, and Reviewers.

The PR body should include at least Summary, Validation, Risk/limitations, and related Issues. You can use Fixes #123 to arrange for an Issue to close automatically after merging. If the project has no PR template, contributors should proactively include compositor compatibility, the Fcitx5 version, screenshots, and platforms that “have not yet been tested.”

3.2 Using gh pr create

Once you are in the repository directory and the current branch has been pushed, you can use gh to create a PR. For a shared repository, specify only the branch name as the head; for fork-and-pull, also specify the fork’s owner:

gh pr create \
  --repo OWNER/REPOSITORY \
  --base main \
  --head YOUR_GITHUB_LOGIN:fix/123-compositor-note \
  --title "docs: clarify compositor contribution status" \
  --body $'## Summary\n- Clarify tested and untested compositor paths.\n\n## Validation\n- Reviewed README and local documentation.\n\nFixes #123'

After creating it, check the PR and CI with the following commands:

gh pr checks 123 --watch

3.3 Keeping a PR reviewable

Ideally, a PR should address one logical problem that can be clearly explained:

  • Do not mix formatting, renaming, dependency upgrades, and functional fixes.
  • Do not include unrelated local configuration, credentials, build artifacts, or temporary debugging files in the diff.
  • Changes to workflows, permissions, Action versions, or build and release paths must be called out separately in the description.
  • Documentation contributions also need links, commands, and verification dates; do not present unverified desktop support as fact.

4. Reviews, Request changes, and updating a PR#

4.1 The three review outcomes

Reviewers can comment on the PR as a whole or on specific lines, and submit one of three statuses:

Status Meaning Author’s next step
Comment Provides feedback without approving or blocking a merge Discuss, record decisions, and make changes if needed
Approve Approves the current changes for merging Wait for the other requirements to be satisfied
Request changes Identifies issues that must be addressed before merging Fix, explain, or reach an explicit resolution with the reviewer

Whether Request changes actually blocks merging depends on the repository’s branch protection or ruleset. Do not conclude that “the platform definitely prevents merging” from a review status alone. Check required reviews, required checks, and whether the branch is up to date together.

4.2 Submitting a review with gh pr review

Reviewers can submit overall feedback from the terminal:

# 只评论,不批准也不阻止
gh pr review 123 --comment --body "请补充失败路径的验证结果。"

# 修改已完成且满足审查范围
gh pr review 123 --approve --body "已检查 diff 与本地验证。"

# 合并前必须处理的问题
gh pr review 123 --request-changes --body "请先修复 workflow 权限,再重新请求 review。"

Comments on specific lines are usually better made on the web interface’s Files changed page; terminal commands suit overall conclusions or automation support. Reviews should focus on verifiable behavior, regression risk, and maintenance costs, rather than replacing project rules with personal coding preferences.

4.3 Responding to feedback as the author

Classify each comment: requires a change, requires an explanation, is outside the current scope, or belongs in a separate Issue. After making changes, push to the same head branch; the PR will update automatically and rerun the relevant checks:

git switch fix/123-compositor-note
# 编辑并运行本地检查
git diff --check
git add path/to/changed-file
git commit -m "docs: address review feedback"
git push
gh pr view 123 --comments

After a substantial round of changes, you can select Re-request review on the website. Resolving a conversation does not mean the code has been validated; describe “what changed, which commands verified it, and which checks still fail” in your reply.

If you need to realign the topic branch with the base:

git fetch upstream
git rebase upstream/main
# 解决冲突、运行检查后
git push --force-with-lease -u origin HEAD

Do this only when the people using the head branch are aware of it and the remote really is yours. If a maintainer has already committed to the branch, prefer a merge or let the maintainer decide how to synchronize it.

5. Required checks, CI, and merging#

5.1 Treat CI as a merge requirement, not decoration

A PR’s Checks page displays automated tests, builds, formatting checks, and security scans. Required checks are statuses that the base branch’s rules require to pass. Passing local checks does not mean the checks on GitHub have passed, nor that permission, matrix job, and merge queue requirements have been met.

Before merging, confirm that the PR is not a Draft, required reviews are satisfied, required checks have succeeded, and base/head have no conflicts and satisfy any up-to-date requirement. Also confirm that fork workflows have not been left incomplete because of permissions, approval, or cancellation, and check again that the PR diff still corresponds to the Issue.

Example command-line checks:

gh pr checks 123 --required
gh pr checks 123 --watch
gh pr view 123 --json state,isDraft,mergeable,reviewDecision,statusCheckRollup,baseRefName,headRefName

If CI fails, first distinguish among three causes: code or test failures, workflow configuration failures, and transient runner/dependency problems. Do not hide deterministic failures behind reruns, or remove checks or broaden permissions just to “make it green.”

5.2 Choosing a merge method

The final merge method is determined by repository rules and maintainers:

Method Result Common use cases
Merge commit Preserves the individual commits in the PR and adds a merge point When the full branch history must be retained
Squash and merge Compresses the PR into a single commit on the base When one PR represents one logical change
Rebase and merge Places the commits linearly on the base When the commits are already well organized and the project requires a linear history

Do not bypass repository settings because of personal preference. If you have merge access and have confirmed that all requirements are met, gh pr merge lets you explicitly select a method:

# 由维护者确认后执行;会压缩并删除 head branch
gh pr merge 123 --squash --delete-branch

# 满足规则后自动合并(仓库允许且项目方明确使用时)
gh pr merge 123 --auto --squash --delete-branch

If you are a contributor without write access, submit the PR and wait for a maintainer to merge it; do not try to bypass the rules with --admin. The merged PR, comments, and commit history remain available even if the head branch is deleted.

6. Synchronizing and cleaning up after merging#

Confirm that the PR has been merged before deleting local and remote topic branches. In a shared repository, update from origin/main; in fork-and-pull, the usual procedure is:

git switch main
git fetch upstream
git merge --ff-only upstream/main
git push origin main
# 先确认 PR 已合并且分支提交不再需要;squash/rebase 合并后 -d 可能拒绝。
git branch -d fix/123-compositor-note
git push origin --delete fix/123-compositor-note

Some repositories automatically delete the remote head branch after merging, in which case the final deletion command may report that the branch does not exist. Check with git branch -r or on GitHub first; do not force an operation merely to eliminate the message. If a PR is closed without merging, keep the branch and commits until you have confirmed they are no longer needed.

7. Security boundaries for fork PRs and Actions#

Code in a fork PR comes from outside the current repository’s control, so maintainers must treat it as untrusted input. Fork workflows triggered by pull_request in public repositories generally do not have access to ordinary repository Secrets, and GITHUB_TOKEN permissions should be considered read-only; the exact permissions still depend on repository and event configuration. Workflows from first-time contributors may also require maintainer approval before running.

Both contributors and maintainers should respect these boundaries:

  • Do not include PATs, SSH private keys, cloud keys, or other credentials in PRs, Issues, command arguments, or logs.
  • Do not inject write-enabled tokens or production Secrets into ordinary test jobs just to let fork PRs publish artifacts.
  • Review changes to .github/workflows/, especially run:, third-party Actions, permission declarations, and artifact handling.
  • Use pull_request_target cautiously: it runs in the base repository’s context and may have Secrets or elevated permissions. Do not check out and execute untrusted fork code in such a privileged workflow.
  • Workflows that publish, deploy, or write to the repository should be separate from tests for untrusted PRs, and should run in controlled events or environments.

Security cannot be reduced to “forks have no Secrets, so anything is safe to run.” Workflows themselves, dependency installation, build scripts, and uploaded artifacts can all execute contributor-supplied content; review least-privilege permissions and event selection together.

8. Common pitfalls at a glance#

Pitfall Consequence Correction
Editing main directly Cannot isolate changes for review; may trigger an unintended release Create a topic branch from the latest base
Forgetting to check base/head PR targets the wrong repository or branch Check the repository, branch, and diff before creating the PR
Configuring only origin for a fork Unclear how to fetch updates from the original repository origin points to the fork; upstream points to the original repository
Integrating changes indiscriminately with git pull Unexpected merge commits or overwritten uncommitted work Run fetch first, then use --ff-only, merge, or rebase according to project policy
Running push --force on shared branches Overwrites other people’s commits Never do this on shared branches; even personal PR branches should use only --force-with-lease
Including unrelated formatting in a PR Noisy reviews and hard-to-trace regressions Split it into a separate PR or separate commits
Writing only “tested” Others cannot reproduce the validation Provide commands, versions, platforms, results, and limitations
Deleting checks when CI fails Defeats the quality gate First determine whether the cause lies in the code, workflow, or infrastructure
Treating Request changes as an absolute lock Misunderstands the platform’s rules Check branch protection/rulesets and review permissions together
Treating pull_request_target as an ordinary PR event Untrusted code may gain access to a privileged context Do not execute fork code in privileged workflows

Official resources#

References & further reading

  1. GitHub Flow ↗
  2. Pull requests ↗
  3. About pull request reviews ↗
  4. Pull request merges ↗
  5. Merging a pull request ↗
  6. Syncing a fork ↗
  7. Secure use reference for GitHub Actions ↗
  8. Using secrets in GitHub Actions ↗
  9. gh pr create ↗
  10. gh pr review ↗
  11. gh pr merge ↗
  12. gh pr checks ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

GitHub repositories and collaboration

  1. 01GitHub basics
  2. 02GitHub repository management
  3. 03The GitHub contribution workflow
CONTINUE READING / NEXT ARTICLEThe GitHub Actions execution model

Understand events, workflows, jobs, runners, and steps, along with the distinct roles of matrices, caches, and artifacts.