Manage GitHub Labels Safely with gh label
Twelve near-duplicate labels and no idea which repository you are about to change: gh label tames both from the terminal. In about ten minutes you can inspect labels, create one, edit or rename one, clone a set between repositories, and delete one deliberately. The examples use gh 2.45.0, the version installed on the system used for this guide.
The route
Jump straight to the step you need, or tick off Done means at the end.
- You need: an authenticated
ghsession with permission to change labels in the target repository. - No
sudo: every command uses GitHub API permissions.
1. Check the target before changing it
Every subcommand accepts -R or --repo with an optional host, owner and repository. Use the complete value in every example, so a shell directory or an unexpected current repository cannot send a change elsewhere.
gh auth status
gh label list --repo OWNER/REPOSITORY --limit 100
Replace OWNER/REPOSITORY with a real repository such as octocat/hello-world. The list command fetches at most 30 labels by default, so raise --limit for a fuller inventory. A successful response is a table of label names, colours and descriptions.
Warning
If the command reports an authentication or permission error, stop and fix access. Do not retry with a write command.
2. Confirm the inventory
For a machine-readable check, ask for selected fields and filter them with --jq:
gh label list --repo OWNER/REPOSITORY --json name,color,description \
--jq '.[] | [.name, .color, (.description // "")] | @tsv'
The JSON fields include the name, colour, description, default status, creation and update times, and URL. Use --search bug to search names and descriptions.
Tip
Search results are sorted by relevance, so --sort and --order do not control that result set.
3. Create a label with explicit metadata
Creating a label changes the remote repository. Choose the name, description and six-character hexadecimal colour first. Write the colour without a leading hash.
gh label create "needs reproduction" \
--repo OWNER/REPOSITORY \
--description "A report needs a small reproducible case" \
--color D4C5F9
On success, gh reports that the label was created. Leave out the colour option and gh picks a random colour, which makes repeatable setup hard to review. If the label already exists, use --force only when you mean to update its colour and description:
gh label create "needs reproduction" \
--repo OWNER/REPOSITORY \
--description "A report needs a small reproducible case" \
--color D4C5F9 \
--force
Verify the resulting record instead of trusting the short status message:
gh label list --repo OWNER/REPOSITORY --search "needs reproduction" \
--json name,color,description
4. Edit or rename an existing label
gh label edit changes the label named by its positional argument. Set a colour, a description, a new name, or any combination. A rename still changes the remote repository, so check the current spelling first.
gh label edit "needs reproduction" \
--repo OWNER/REPOSITORY \
--name "needs-reproduction" \
--color BFDADC \
--description "A report needs a small reproducible case"
Editing has no --force flag. If the original name is wrong, the command fails rather than guessing. Confirm both the new name and its metadata:
gh label list --repo OWNER/REPOSITORY --search "needs-reproduction" \
--json name,color,description
5. Clone labels without overwriting by accident
Use gh label clone SOURCE-OWNER/SOURCE-REPOSITORY to copy labels from one repository. The destination defaults to the current repository, but an explicit --repo makes it obvious:
gh label clone SOURCE-OWNER/SOURCE-REPOSITORY \
--repo DEST-OWNER/DEST-REPOSITORY
By default:
- Destination-only labels stay. Nothing absent from the source is deleted.
- Existing labels are skipped. That is the safer choice for a first pass.
Warning
The next option overwrites matching destination labels, metadata included. Review both inventories first and use it only when synchronisation is intended.
gh label clone SOURCE-OWNER/SOURCE-REPOSITORY \
--repo DEST-OWNER/DEST-REPOSITORY \
--force
Cloning is not a two-way synchronisation, and it never removes destination-only labels. Re-run the destination list command and compare the names and metadata you expected.
6. Delete only with a recovery plan
Deletion is irreversible through gh label. It removes the label from the repository, and the command asks for confirmation by default. Name one label and one repository explicitly, and do not wrap it in a broad shell loop.
gh label delete "needs-reproduction" --repo OWNER/REPOSITORY
Read the confirmation prompt and answer only if the target is correct. For non-interactive use, --yes skips the prompt:
gh label delete "needs-reproduction" \
--repo OWNER/REPOSITORY \
--yes
Recovery
There is no undo command in this group. Before deleting a label used by automation or issue templates, record its name, colour and description with gh label list --json, then recreate it with gh label create using those values. Issues that used the old label may need separate review.
Common traps
- Wrong repository:
--repois the simplest guard against changing whichever repository happens to be current. Include it in scripts and copy-and-paste commands. - Colour format: use exactly six hexadecimal characters such as
FF0000, not a CSS value with#. - Truncated lists: the default list limit is 30. Use a larger
--limitwhen checking for duplicates. - Search ordering:
--searchreturns best matches, not the requested name or creation order. Inspect the returned JSON before editing. - Privileges: none of these commands needs
sudo. Elevated local privileges cannot repair a GitHub authorisation failure.
Done means
- Right repository: you listed the intended repository with an explicit
--repovalue. - Labels verified: any created or edited label has checked name, colour and description values.
- Clone reviewed: you understand which labels were skipped, overwritten or left as destination-only.
- Deletion deliberate: it was confirmed by hand, with enough metadata recorded to recreate the label if needed.