---
title: Merge Schedules
description: How the merge queue tests a pull request before its merge schedule opens, and how it keeps that pull request from holding up the ones that can merge now.
---

A [queue rule](/merge-queue/rules) can limit when its pull requests merge with a
[`schedule`](/configuration/data-types#schedule) in its `merge_conditions`.
Outside that window, a pull request's merge schedule is closed: it stays in the
queue, and Mergify does not merge it, however green its checks are.

Mergify still runs the pull request's checks before the window opens, so it
merges as soon as the window does. It also keeps the pull request from holding
up the ones whose schedule is open: they are tested first, and a pull request
waiting for its window gives way to them wherever it stands in their way, even
in the same [batch](/merge-queue/batches).

```yaml
queue_rules:
  - name: dependencies
    queue_conditions:
      - author = dependabot[bot]
    merge_conditions:
      - check-success = ci
      - or:
          - label = hotfix
          - schedule = Mon-Fri 09:00-11:00[Europe/Paris]
  - name: default
    merge_conditions:
      - check-success = ci
```

Here, dependency updates merge on weekday mornings, unless labeled `hotfix`,
and every other pull request merges as soon as its checks pass. The examples
below use this configuration: #21 is a dependency update queued overnight, and
#22, #23, #24 and #25 come from your team.

## Which schedules Mergify recognizes

Mergify treats a merge schedule as closed only when the queue rule's conditions
prove it:

- A `schedule` or [`current-datetime`](/configuration/conditions#attributes-list)
  condition listed in `merge_conditions`, or nested in an `and`, closes the rule
  for every pull request outside its window.

- In an `or` with `label` or `author` conditions, as above, it closes the rule
  only for the pull requests that match none of them. A dependency update
  labeled `hotfix` has its schedule open at any hour.

When a schedule sits in an `or` with any other condition, Mergify cannot tell in
advance, and treats the pull request as if its schedule were open: it is tested
in its turn, and if the window is closed when its checks pass, it waits at its
place in the queue like a pull request with any other unmet merge condition.
Nothing else on this page applies to it.

A [scheduled freeze](/merge-protections/freeze) is not a merge schedule. A
batch whose checks pass during a freeze waits for the freeze, not for its
schedule, and gives up nothing described here, even if its schedule is closed
too.

## Testing before the window opens

While pull requests whose schedule is open are waiting to be tested, one whose
schedule is closed steps aside: it keeps its place in the queue, and they are
tested first. Once none is left waiting, Mergify tests it anyway, behind every
pull request that can merge now, so its checks run before its window opens.
When they pass, its batch waits with the
[batch status](/configuration/data-types#batch-status) `waiting_schedule`, and
merges when the window opens.

> **Figure:** #21 was queued first, but its schedule is closed: #22 and #23 are tested first, and #21 runs its checks behind them before its window opens, so it blocks neither.
>
> Two rows of queue positions, front of the queue on the left. Queued in the order 21, 22, 23, where 21 is a dependency update whose schedule is closed at night. Tested in the order 22, 23, 21: the two pull requests that can merge now go first, and 21 runs its checks behind them, ahead of its window.

Several pull requests whose schedules are closed are tested in the order their
windows open, then by [priority](/merge-queue/priority): one waiting for 08:00
is tested ahead of one waiting for 09:00, so it is not tested on top of a pull
request that cannot merge before it. A [stacked](/merge-queue/stacks) pull
request is ordered as if its window opened no earlier than those of the pull
requests it is stacked on, since it cannot merge before them. In a few cases,
such as when a priority rule lets one of them interrupt checks, priority alone
orders them.

## Giving up its place in the queue

A batch is tested on top of the batches ahead of it, so it cannot merge before
them. A batch waiting for its schedule would hold up every batch behind it
until its window opens. Instead, as soon as a batch behind it can merge (its
checks passed and its schedule is open), the waiting batch gives up its
place. Mergify takes it, and every batch tested on top of it, back into the
queue: the pull requests that can merge are tested again on the base branch and
merge, and the waiting pull requests are tested again behind them.

Say #21 started its checks at 10:50, inside its window, and #22 was batched
behind it. The window closes at 11:00, before #21's checks end. #21 passes and
waits for 09:00 the next morning, while #22 passes on top of it.

> **Figure:** #22 passed its checks, but they ran on top of #21, which cannot merge before 09:00 the next morning. #21 gives up its place: #22 is tested again on its own and merges, and #21 runs its checks again behind it.
>
> Two rows of queue positions, front of the queue on the left. After 11:00, 21 has passed its checks and waits for its schedule, and 22 behind it has passed its own checks, run on top of 21. After 21 gives way, 22 is tested again first, on the base branch, and 21 is tested again behind it.

The waiting batch also gives way before a batch behind it can merge, in two
cases:

- **A batch that can merge has not started its checks yet.** A pull request
  whose schedule is open is batched behind a batch waiting for its schedule, or
  still running its checks ahead of it. The waiting batch gives way before the
  new batch spends a CI run on top of it. Only the waiting batches the new one
  would still be tested on top of are taken back: in
  [parallel mode](/merge-queue/queue-modes#parallel-mode), one in another scope
  keeps its place.

- **Both schedules are closed, and the batch behind opens first.** A batch
  waiting for 08:00 behind one waiting for 09:00 would be tested again at 08:00
  and merge a CI run late. The 09:00 batch gives way while both are still
  closed, so the 08:00 batch is tested again overnight and merges at 08:00.
  Mergify only does this when the rebuilt queue cannot put the 08:00 batch
  behind a batch still closed at 08:00, or it would wait behind another batch
  again. Any other batch waiting for a later window that it could end up
  behind gives way at the same time.

A batch does not give way when every pull request of the batch behind it is
stacked on one of its pull requests: rebuilt, that batch would wait behind them
again.

## Giving up its check slot

[`max_parallel_checks`](/merge-queue/performance#parallel-checks) caps how many
batches run their checks at once. A batch tested ahead of its schedule only
borrows a slot that no pull request needed when it started. So when every slot
is taken and the next pull request waiting to be tested has its schedule open, a
batch running its checks ahead of its schedule gives its check slot away:
Mergify takes it back into the queue and tests the waiting pull request in its
slot. It is tested again once a slot is free.

This happens whatever
[`allow_checks_interruption`](/merge-queue/priority#managing-check-interruptions-based-on-priority)
is set to: that option decides what a higher priority does to running checks,
and a schedule is not a priority.

> **Figure:** With every slot taken, #23 can merge now but has nowhere to run its checks. #21, tested ahead of its window, gives its slot to #23 and runs its checks again once a slot is free.
>
> Two rows of three check slots and a waiting column, with max_parallel_checks set to 3. First row: 21, a dependency update whose schedule is closed, runs its checks in slot 1, 24 and 25 run theirs in slots 2 and 3, and 23, whose schedule is open, waits. Second row: 23 runs its checks in slot 1, 24 and 25 keep their slots, and 21 waits.

When several batches could give their slot, the one the waiting pull request
would be tested on top of goes first, since it would give way before that pull
request's checks start anyway. Otherwise, the batch whose window opens last
gives its slot, and among those opening together, the last one in the queue.

A batch keeps its check slot when:

- **The waiting pull request's schedule is closed too**, even if its window
  opens sooner. The running batch frees its slot when its checks end, so taking
  it away would cost a CI run to gain at most one.

- **A batch behind it can merge now.** Giving the slot away takes every batch
  behind it back into the queue too, so a batch gives its slot only when every
  batch behind it is waiting for its schedule as well.

- **The waiting pull request cannot merge now anyway**: it is stacked on a pull
  request of a batch whose schedule is closed, or a
  [scheduled freeze](/merge-protections/freeze) holds it.

- **A failed batch is being
  [split](/merge-queue/batches#handling-batch-failure-or-timeout) to find the
  pull request that broke it.** The
  split's sub-batches keep their slots, and no batch ahead of them gives its
  own.

Only `max_parallel_checks` triggers this: a pull request waiting for room in a
scope whose own [capacity](/merge-queue/queue-modes#limiting-concurrency-per-scope)
is full does not take a slot this way.

## Sharing the same batch

Mergify keeps a pull request whose schedule is closed out of a batch of pull
requests that can merge now while the batch forms. A batch can still end up
holding both. Say #21 and #26, two dependency updates, are batched together at
10:50, and the window closes at 11:00 before their checks end. The batch waits
with both for 09:00 the next morning. If #26 is then labeled `hotfix`,
its schedule is open, but it shares the same batch as #21, whose schedule is
not.

Mergify then splits the batch: it takes it back into the queue, #26 is tested
again on its own and merges, and #21 is tested again behind it.

> **Figure:** #26 was labeled hotfix after its batch began waiting for 09:00. The batch is split: #26 is tested on its own and merges, and #21 is tested again behind it.
>
> Two rows of queue positions, front of the queue on the left. First row: one batch holding 21 and 26 waits for its schedule, which opens at 09:00. Second row, after 26 is labeled hotfix and the batch is split: 26 runs its checks alone first, and 21 runs its checks again behind it.

Splitting costs both pull requests a new CI run, so Mergify only splits when the
pull requests that can merge would merge sooner, and the others no later than
their window allows:

- The batch ran its checks on a [batch pull request](/merge-queue/batches), which tells
  Mergify how long the new runs take.

- The window of the pull requests that cannot merge opens after both new runs
  would have ended. If the window opens sooner, the unsplit batch merges sooner.

- The pull requests that can merge still have their schedule open when their
  new run ends, are
  not stacked on a pull request whose schedule is closed, and can start their
  checks at once: a check slot is free for them.

- No batch ahead or behind has failed, no batch ahead is frozen, and every batch
  ahead that the pull requests would still be tested on top of has its schedule
  open, now and when their new run ends.

## What you see on the pull request

Each of these moves interrupts the checks of the waiting
pull request and of every pull request batched with it or tested on top of it.
Each of them reports the
[`schedule-blocked-ahead-yielded`](/configuration/data-types#queue-dequeue-reason)
dequeue reason and stays in the queue: it is tested again without you doing
anything.
