Repository Setup - Creating, Migrating, and Governing Forgejo Repositories
Status: Active Last Updated: 2026-08-26 Category: CI/CD - Forgejo Administration Prerequisites: forgejo-installation, user-management Time: 1-2 hours Tags: forgejo, repositories, migration, branches, templates, git
Summary
This article covers creating repositories in Forgejo (empty, from template, or migrated), setting default branches, configuring protected branches before CI runs, and the housekeeping settings โ visibility, LFS, and issue trackers โ that determine how smoothly Woodpecker can work with a repo.
๐ฏ What You'll Learn
By the end of this article, you'll be able to:
- โ Create repos via UI, API, and push an existing project into them
- โ Migrate repos from GitHub/GitLab/other Gitea instances with issues and PRs
- โ Set sensible defaults: branch name, visibility, merge style
- โ Configure basic branch protection ahead of enabling CI
- โ Use repo templates to standardize new projects
Context / Why This Matters
Woodpecker discovers pipelines by reading .woodpecker.yaml from your repo's default branch. That means repository configuration โ default branch, protection rules, webhook wiring โ directly controls what CI builds and when. Setting up repos consistently also makes org-wide automation (secrets, webhooks, templates) predictable instead of per-repo archaeology.
Implementation / Core Content
Creating a Repository
UI path: + โ New Repository. Key decisions at creation time:
- Owner: prefer the organization (
acme-corp) over a personal account. - Visibility: private unless the code is genuinely public; internal (org-visible) is a good middle ground.
- Initialize: check "Initialize Repository" only for brand-new projects; for existing code leave it empty and push.
Via API:
curl -s -X POST "https://git.example.com/api/v1/orgs/acme-corp/repos" \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "api-service",
"private": true,
"default_branch": "main",
"auto_init": false,
"gitignores": "Go",
"issue_labels": "Default"
}'
Push existing code in:
cd my-project
git remote rename origin old-origin 2>/dev/null || true
git init -b main 2>/dev/null || true
git add -A && git commit -m "initial import" || true
git remote add origin git@git.example.com:acme-corp/api-service.git
git push -u origin --all
git push origin --tags
Default Branch
The default branch is what CI sees on every push-based trigger and what PRs target by default. Set it at creation, or change later: Repo โ Settings โ Branches โ "Default Branch". Changing it does not move CI config expectations automatically โ make sure .woodpecker.yaml exists on the new default.
# Via API
curl -s -X PATCH "https://git.example.com/api/v1/repos/acme-corp/api-service" \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"default_branch":"main"}'
Convention for fogserv homelab projects: main as trunk, release/x.y branches cut per release line, feature branches named type/ticket-slug.
Migrating Repositories
Forgejo's built-in migrator copies git data plus optionally issues, PRs, labels, milestones, releases, and wiki.
UI path: + โ New Migration, pick source type (GitHub, GitLab, Gitea/Forgejo, Git only, ...). For a GitHub source you'll typically need a read token if repos are private.
CLI alternative (server-side):
forgejo migrate \
--auth-token "$GITHUB_TOKEN" \
--repo-source url=https://github.com/acme/api-service \
--repo-destination acme-corp/api-service \
--issues --pulls --releases --lfs
Migration checklist:
- Create the destination repo first (or let the migrator create it).
- Enable LFS on both sides if the source uses it.
- After migration, verify:
git log, open PR count, releases list. - Re-point any external integrations (webhooks, badges) at the new URL.
- Keep the source read-only/archive it to avoid split-brain commits.
Protected Branches (Baseline)
Full policy discussion lives in branch-protection, but every repo should have, minimum, on its default branch:
- Status checks required once CI is wired up (e.g.
ci:woodpecker). - Dismiss stale approvals on new pushes.
- No force-push, no deletion.
Repo โ Settings โ Branches โ Add New Rule, pattern main. Doing this before the first CI run prevents accidental direct pushes that bypass pipeline validation.
Repository Templates
Any repo can be marked as a template (Settings โ check "Template"). Template contents include the file tree and optionally variables like {REPO_NAME} in filenames/content. Standardize your .woodpecker.yaml, Dockerfile, and linting config this way so new projects start CI-ready:
acme-corp/.repo-template
โโโ .woodpecker.yaml
โโโ Dockerfile
โโโ .editorconfig
โโโ README.md
New repo โ "Use Template" dropdown at creation.
Housekeeping Settings Worth Reviewing Per Repo
| Setting | Recommendation |
|---|---|
| Merge style | Allow squash + merge commit; disable rebase-only if history discipline is weak |
| Issues/PRs | Disable unused units to reduce noise |
| Packages | Enable if using Forgejo's container registry (OCI) |
| LFS | Enable for binaries >~1MB; configure .gitattributes first |
| Archive | Set ARCHIVE_UNITS or archive finished projects rather than deleting |
Practical Examples
Example 1: Full setup of a new service repo
# 1. Create repo under the org
curl -s -X POST "https://git.example.com/api/v1/orgs/acme-corp/repos" \
-H "Authorization: token $FORGEJO_TOKEN" -H "Content-Type: application/json" \
-d '{"name":"worker","private":true,"default_branch":"main","auto_init":false}'
# 2. Push code
git remote add origin git@git.example.com:acme-corp/worker.git
git push -u origin main
# 3. Add pipeline skeleton
cat > .woodpecker.yaml <<'EOF'
when:
event: [push, pull_request]
steps:
test:
image: golang:1.22
commands:
- go vet ./...
- go test ./...
EOF
git add .woodpecker.yaml && git commit -m "ci: add woodpecker pipeline" && git push
# 4. Confirm Woodpecker picked it up (see woodpecker-first-pipeline)
Example 2: Verify a migration integrity spot-check
git clone git@git.example.com:acme-corp/api-service.git && cd api-service
git log --oneline | wc -l # compare with source repo
git tag | tail -5 # tags carried over?
ls .git/lfs 2>/dev/null && echo "LFS present"
Common Pitfalls & Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
Push rejected pre-receive hook declined |
Branch protection blocks direct push | Open a PR, or adjust rule; see branch-protection |
| CI never triggers on new repo | Webhook not created / wrong default branch | Re-enable app in Woodpecker or add webhook manually; see webhooks |
| Migration missing issues/PRs | Options unchecked or source API rate-limited | Re-run migration with options; use a token to lift rate limits |
| Default branch changed but CI still builds old one | Pipeline file only exists on old branch | Commit .woodpecker.yaml to the new default branch |
| Large push times out over HTTP | Big files without LFS | Enable LFS and git lfs migrate import --everything |
| Clone asks for password repeatedly | HTTPS remote without credential helper | Switch to SSH remote or store token via git config credential.helper |
Next Steps / Ops Actions
- Wire CI triggers properly: webhooks
- Write your first pipeline: woodpecker-first-pipeline then woodpecker-pipeline-testing
- Formalize merge policy: branch-protection
Sources & Related Articles
External references consulted:
- https://forgejo.org/docs/latest/user/repo-settings/
- https://forgejo.org/docs/latest/admin/command-line/#migrate
- https://forgejo.org/docs/latest/user/packages/container/
Related knowledge-base articles:
Change Log
2026-08-26
- Initial creation covering repo creation, migration, default branches, baseline protection, and templates.