Skip to content

subtree split --rejoin hangs when merge commits exist between rejoin and HEAD #6203

@nalmiron

Description

@nalmiron

Existing issues matching what you're seeing

  • I was not able to find an open or closed issue matching what I'm seeing

Git for Windows version

git version 2.54.0.windows.1

Windows version

Windows 11

Windows CPU architecture

x86_64 (64-bit)

Additional Windows version information

Microsoft Windows [Version 10.0.26200.8246]

Options set during installation

Default installation options

Other interesting things

Using git subtree with --rejoin to manage a subdirectory prefix in a repo with ~500 total commits, ~200 touching the subtree prefix. The repo has merge commits from normal git pull operations (not squash merges).

Also tested on Git 2.53.0.vfs.0.7 (Microsoft.Git ARM64) — same behavior. The cs/subtree-split-fixes patch in 2.54.0 (removing should_ignore_subtree_split_commit) does not address this issue.

Terminal/shell

PowerShell 5.1 / Git Bash (MSYS2)

Commands that trigger the issue

1. git subtree push --prefix=<subdir> --rejoin <remote> <branch>  # succeeds, creates rejoin marker
2. # make some local commits
3. git pull  # creates a merge commit (normal pull merge from origin)
4. # make another commit touching the subtree prefix
5. git subtree push --prefix=<subdir> --rejoin <remote> <branch>  # hangs indefinitely
   # Also hangs: git subtree split --prefix=<subdir> --rejoin -b test-branch

Expected behaviour

The split should complete in seconds (only a few new commits since the last rejoin). With --ignore-joins (which bypasses rejoin optimization and processes all ~500 commits linearly), it completes in ~5 minutes — confirming the issue is specific to the rejoin recursion path, not the content.

Actual behaviour

The script hangs indefinitely at the process_split_commit stage. With GIT_TRACE=1, the last operation is a git rev-parse <hash>^@ call inside a deeply nested recursive check_parentsprocess_split_commit chain (~185 levels deep). Bash exhausts its process table for $() subshell spawning and the script stops making progress.

Root cause

When process_split_commit encounters a commit whose parents aren't in the per-run temp cache ($GIT_DIR/subtree-cache/$$), check_parents recursively calls process_split_commit for each uncached parent. Each recursive call spawns subshells for git rev-parse "$rev^@" via $() command substitution.

The pull merge introduces a second parent whose ancestry reaches pre-rejoin commits through a path not covered by the find_existing_splits exclusion (^mainline-parent). This forces recursion through ~200 uncached ancestors. At ~185 levels of nested bash function calls with $() subshells, the process table is exhausted and the script hangs.

The find_existing_splits exclusion ($unrevs) correctly limits the rev-list, but check_parents doesn't consult $unrevs — it only checks the temp cache. So excluded commits that the rev-list skips still trigger recursion when encountered as parents of merge commits in the processing window.

Repository

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions