docs(pijul): add channel merge and unmerge workflow guide

Chasesomero
Mar 12, 2026, 9:25 PM
XSOOHSO4A7VPJWWUYULAPCDQNVDOYPRQLKUW24NQIYHQSBJAR76QC

Dependencies

  • [2] WHWKBNW2
  • [3] 32ICZXL4 init laravel zero for fun
  • [4] 44YOENAC docs(organize): save cleanup idea for later and organize personal first-time stuff to different file

Change contents

  • edit in README.md at line 95
    [4.725]
    [3.314483]
    ## Feature merge/unmerge workflow with a baseline channel
    See [Pijul feature channel merge and unmerge workflows](./PIJUL_FEATURE_CHANNEL_WORKFLOWS.md) for a simple recipe that uses a cleaner baseline such as `mainer-main`, feature channels such as `feature-login`, and an integration channel such as `staging`.
  • file addition: PIJUL_FEATURE_CHANNEL_WORKFLOWS.md (----------)
    [2.1]
    # 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`.