# 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`.