Search code examples
gitgit-mergegit-subtreecherry-pickmonorepo

Git commits that touch a subtree/subfolder


I have a monorepo project proj using subtrees in a folder sub under proj/sub. I have made tons of commits that touch both proj and sub. How do I publish the relevant changes to upstream sub effectively?

Usually, I'd have to cherry-pick every commit using

git cherry-pick -x --strategy=subtree -Xsubtree=sub/ commit-ref

but I have made zillions of commits so this is unfeasible. How can I integrate the changes to sub at once? For instance, create one big squashed commit that would bring sub to the same state it is in my monorepo.

Related: View commits that make changes to subfolder, How to cherry pick a range of commits and merge into another branch


Solution

  • Current solution: mostly rebase

    A few rebases with a couple of cherrypicks will do, but so far I haven't found an automated way using a single command. Use

    git rebase -s subtree -Xsubtree=sub --onto sub_master proj_ini proj_end
    

    to copy onto the subproject master branch sub_master all toplevel proj commits from ref proj_ini (exclusive) to ref proj_end (inclusive). The branch where you are at doesn't matter. For each commit, these things may occur:

    1. Commits that touch only the subproject sub will be copied cleanly

    2. Commits that touch only files outside sub will not show up. Since we are rebasing, these are silently ignored. If you would cherry-pick this type of commit, an error will occur telling that empty commits are not allowed (future Git versions may have git cherry-pick --skip-empty)

    3. Commits that have changes to both sub and outside it will be copied with the exact same message, but with only the relevant changes/files kept, the rest being cleanly ignored

    4. Renames/moves from proj to sub are cleanly rewritten as creations

    5. Merge commits are mostly cleanly accounted for, with some exceptions

      5.1 Explicitly ignore the latest merge commit you did to the proj master coming from sub.

      5.2 Explicitly merge certain merge commits that had involved conflict resolution, using a cherrypick (current branch must be sub_master):

       git cherry-pick -x --strategy=subtree -Xsubtree=sub -m 2 merge_ref
      

      where -m is usually 1 or 2 for each parent. (In practice I will blindly try either one and git cherry-pick --abort and try the other if I get conflicts or unwanted changes)