# Pijul feature channel merge and unmerge workflows

This guide is written for both humans and automation/agents.

It describes a simple way to:

- keep a cleaner, more authoritative baseline channel such as `mainer-main`,
- pull a feature channel into an integration channel such as `staging`, and
- later remove that feature from `staging` without throwing away unrelated work.

## The three-channel model

Use three kinds of channels:

- `mainer-main`: your cleaner baseline or "keep set". It can be ahead of `staging`, but it must not include the feature you may want to remove later.
- `feature-login`: a channel that represents one feature. Keep it around if you may want to add or remove that feature later.
- `staging`: a mix-and-match channel where you pull features in and out.

The key idea is:

> To remove a feature later, compute `feature - mainer-main`, then unrecord those hashes from `staging`.

## Important command choice: `fork` vs `channel new`

Use `pijul fork` when you want a new channel that starts from an existing channel:

```bash
pijul fork --channel main mainer-main
pijul fork --channel mainer-main staging
pijul fork --channel mainer-main feature-login
```

Use `pijul channel new` only when you want an empty channel.

## One-time setup

If you do not already have the channels you want:

```bash
# Make a baseline copy of your main line.
pijul fork --channel main mainer-main

# Make an integration channel from that baseline.
pijul fork --channel mainer-main staging

# Make a feature channel from the same baseline.
pijul fork --channel mainer-main feature-login
```

If those channels already exist, do not recreate them.

## Workflow 1: merge a feature into `staging`

### 1. Record the feature on its own channel

```bash
pijul channel switch feature-login
# edit files
pijul record -a -m "feat(login): add login flow"
```

### 2. Pull that feature into `staging`

You can switch to `staging` first:

```bash
pijul channel switch staging
pijul pull . --from-channel feature-login -a --no-prompt
```

Or target `staging` directly without switching:

```bash
pijul pull . --from-channel feature-login --to-channel staging -a --no-prompt
```

### 3. Review what `staging` now has compared to the baseline

```bash
pijul channel switch staging
pijul diff --channel mainer-main --short
pijul log --channel staging --hash-only
```

Useful spot checks:

```bash
pijul log --channel feature-login --hash-only
pijul change <hash>
```

## Workflow 2: remove a feature from `staging`

This is the reverse workflow.

Do **not** pipe every hash from `feature-login` straight to `pijul unrecord`. A feature channel usually contains shared base history too, and you do not want to remove that from `staging`.

Instead:

1. compare the feature channel to a baseline that does **not** contain the feature, and
2. unrecord only the hashes that are unique to the feature channel.

### 1. Preview the hashes that belong only to the feature

This prints hashes that are in `feature-login` but not in `mainer-main`:

```bash
comm -13 \
  <(pijul log --channel mainer-main --hash-only | sort) \
  <(pijul log --channel feature-login --hash-only | sort)
```

If this list is empty, either:

- the feature channel has no unique recorded changes, or
- your baseline already contains the feature, which means it is not the right baseline for unmerging.

### 2. Inspect the candidate hashes before removing them

```bash
comm -13 \
  <(pijul log --channel mainer-main --hash-only | sort) \
  <(pijul log --channel feature-login --hash-only | sort) \
| xargs -r -n 1 pijul change
```

### 3. Unrecord those feature-only hashes from `staging`

This Bash snippet gathers the hashes first, then passes them to a single `unrecord` command:

```bash
mapfile -t feature_only_hashes < <(
  comm -13 \
    <(pijul log --channel mainer-main --hash-only | sort) \
    <(pijul log --channel feature-login --hash-only | sort)
)

if ((${#feature_only_hashes[@]})); then
  pijul unrecord --channel staging --reset --no-prompt "${feature_only_hashes[@]}"
else
  echo "No feature-only hashes found."
fi
```

`--reset` updates the working copy so `staging` actually reflects the removed feature.

### 4. Review `staging` after the unmerge

```bash
pijul channel switch staging
pijul diff --channel mainer-main --short
pijul log --channel staging --hash-only
```

## Useful command strings

### Merge a feature into `staging` in one command

```bash
pijul pull . --from-channel feature-login --to-channel staging -a --no-prompt
```

### Print the feature-only hashes

```bash
comm -13 \
  <(pijul log --channel mainer-main --hash-only | sort) \
  <(pijul log --channel feature-login --hash-only | sort)
```

### Inspect why a change cannot be removed cleanly

If `pijul unrecord` refuses because later work depends on a feature hash, inspect dependents:

```bash
pijul dependents <hash>
```

That will show changes that depend on the hash you are trying to remove.

## Rules of thumb

- Keep feature channels around while you may still want to pull or unpull them elsewhere.
- `mainer-main` does not need to be an exact pre-merge snapshot. It can be "mainer" than `staging` as long as it does not contain the feature you want to subtract.
- The baseline is a "keep set": whatever is already in `mainer-main` is treated as something you want to preserve.
- If `staging` has later changes that depend on the feature, `pijul unrecord` may refuse until you also remove those dependent changes.
- `pijul apply` reads change text from standard input, not hash lists. For hash-driven workflows, pass hashes as command arguments instead.

## Short version

Merge in:

```bash
pijul pull . --from-channel feature-login --to-channel staging -a --no-prompt
```

Unmerge out:

```bash
mapfile -t feature_only_hashes < <(
  comm -13 \
    <(pijul log --channel mainer-main --hash-only | sort) \
    <(pijul log --channel feature-login --hash-only | sort)
)

if ((${#feature_only_hashes[@]})); then
  pijul unrecord --channel staging --reset --no-prompt "${feature_only_hashes[@]}"
fi
```

That is the core pattern:

- **merge** with `pijul pull`, and
- **unmerge** with `source-minus-baseline`, then `pijul unrecord --reset`.