# GitHub Actions and Git: A Complete Workflow Guide

> Originally published on [DevToolHub](https://devtoolhub.com/git-github-actions-guide/).

Git and GitHub Actions are two tools most teams adopt without ever deciding how they fit together. You pick a branching habit, wire up a workflow file, and six months later nobody remembers why deploys are slow or why one pull request skipped its tests. This guide covers the decisions that actually matter.

## What does a Git and GitHub Actions workflow actually look like?

A working setup has three layers. Git tracks your code history on short-lived branches. GitHub hosts the shared repository and enforces review rules. GitHub Actions runs automated jobs triggered by events like a push or a pull request.

Every run follows one chain: an event fires (`push`, `pull_request`, `schedule`, `workflow_dispatch`), GitHub matches it against the `on:` block of every file in `.github/workflows/`, matching workflows start their jobs, jobs run in parallel unless `needs:` sets an order, and each job runs its steps on one runner.

## Which Git branching model should you use?

For most teams, use GitHub Flow: one long-lived `main`, short-lived feature branches, merge through a pull request once checks pass. Trunk-based development suits teams shipping continuously. Git Flow only earns its complexity if you ship versioned software to customers running several versions at once.

| Model | Branch structure | Best for | Main cost |
|---|---|---|---|
| GitHub Flow | `main` + short feature branches | Most web teams | Needs solid PR checks |
| Trunk-based | `main` only, feature flags | Many deploys a day | Requires a fast test suite |
| Git Flow | `main`, `develop`, `release/*` | Versioned software | Heavy branch bookkeeping |

Default to GitHub Flow. Move to trunk-based only when your test suite can gate `main` directly.

## When should you rebase instead of merge?

Rebase to clean up your own local commits before you share them. Merge to combine branches other people have already pulled. The Pro Git rule: "Do not rebase commits that exist outside your repository and that people may have based work on."

```bash
git checkout feature/login
git rebase main
```

Rebasing a branch you have already pushed means force-pushing over shared history. Rebase before the first push, merge after.

## What are Git hooks actually good for?

Git hooks run your scripts at points in the Git lifecycle — `pre-commit` for linting, `commit-msg` for message format, `pre-push` for a fast test. Hooks live in `.git/hooks` and are never copied on clone, so share them with `git config core.hooksPath` or a manager like `pre-commit` or Husky. Anyone can skip one with `git commit --no-verify`.

## How are GitHub Actions workflows structured?

A workflow is a YAML file in `.github/workflows/` with `on:` (triggers), `jobs:` (units of work), and inside each job, `runs-on:` and `steps:`. Jobs run in parallel by default.

```yaml
name: CI
on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm test
```

`concurrency` cancels a superseded run. `timeout-minutes` caps a job before the six-hour platform limit.

## Reusable workflows, composite actions, or a matrix?

Use a matrix for the same job across variations. Use a composite action for a repeated step sequence inside one repo. Use a reusable workflow for whole jobs shared across many repos. A matrix caps at 256 jobs per run. Reach for it first.

## How should GitHub Actions handle secrets and cloud auth?

Use OpenID Connect for cloud auth: a short-lived token requested at run time, scoped to that repo and branch. Keep the `GITHUB_TOKEN` read-only by default.

```yaml
permissions:
  contents: read
  id-token: write
```

Organizations created before February 2023 may still default to a read-write token — set it explicitly.

## How does GitHub Actions caching work, and what does it cost?

The `actions/cache` action stores a keyed directory. Each repository gets 10 GB. GitHub deletes caches not accessed in seven days and evicts the oldest first past 10 GB.

```yaml
- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ hashFiles('package-lock.json') }}
    restore-keys: |
      npm-
```

"Anyone who can open a pull request against your repository can read the contents of caches in the base branch." Do not cache secrets.

## How do you enforce tests and reviews?

Enforcement lives in branch protection. Mark status checks as required; the merge button stays disabled until they pass. Required checks match by job name — rename a job and the rule silently stops.

```
/api/        @org/backend
/web/        @org/frontend
*.tf         @org/platform
```

## How do GitHub Actions deployment environments work?

An environment is a named target with its own secrets and protection rules. A job that sets `environment: production` pauses until required reviewers approve or a wait timer elapses.

```yaml
deploy:
  runs-on: ubuntu-latest
  environment: production
  concurrency: production
  steps:
    - run: ./deploy.sh
```

## What actually breaks GitHub Actions in production?

**A compromised action dumps secrets into the logs.** March 2025: `tj-actions/changed-files` (CVE-2025-30066). Every tag repointed to a commit that read secrets from runner memory into public logs. 23,000+ repos affected. Fix: pin to a full commit SHA.

**`pull_request_target` runs fork code with your token.** If the workflow checks out the pull request head and runs anything from it, that is attacker code with your secrets in scope. Use plain `pull_request` for contributor code.

**Caches vanish mid-sprint.** Seven-day eviction plus the 10 GB cap. Symptom: CI time doubles overnight with no code change. Check `gh cache list`.

**Jobs hit a limit and stop.** Killed at six hours. Queued job cancelled after 24 hours. Matrix caps at 256. Concurrent jobs cap at 20 on Free, 40 on Pro, 60 on Team.

## Frequently Asked Questions

**Q: Do I need GitHub Actions if I already use Git?**
A: Git tracks code; GitHub Actions automates what happens to it. For a team, that automation keeps `main` releasable.

**Q: Is rebase or merge better for a team?**
A: Rebase your local commits before the first push. Merge branches once other people have pulled them.

**Q: How do I stop a third-party GitHub Action from stealing secrets?**
A: Pin it to a full commit SHA, not a tag. Set the `GITHUB_TOKEN` read-only and use OIDC.

**Q: Why did my GitHub Actions build suddenly get slower?**
A: Usually cache eviction — seven days unused, or evicted past the 10 GB cap.

**Q: Where should I enforce that tests pass before a merge?**
A: In branch protection, as a required status check. Not the workflow file.

**Quick Summary:**
- GitHub Flow is the right default; trunk-based needs a fast, trusted test suite.
- Rebase local commits before the first push; merge anything already shared.
- Pin third-party actions to a full commit SHA.
- Caches are 10 GB per repo and evicted after seven days unused.
- Real enforcement lives in branch protection.

