0

I'm working on a Rust project that required me to vendor (i.e. fork for use solely with this project) one of the libraries it depends on. To do this, I cloned the git repo of the module I wanted to vendor to a subdirectory of my project, made my changes, and committed them to my local copy of that git repo (which I will call the inner repo). This is now a submodule of the git repo of my project (the outer repo). I would like to flatten them to a single repo. I do not think the changes I've made to this library's code are generally useful enough to warrant publishing as a separate git repo (which is a fancy way of saying it's a horribly messy hack that barely works for my use case), so I would rather have it be an ordinary folder inside the outer repo rather than something I have to clone separately.

I could do this by rm -rf submodule/.git and then git adding the directory from the outer git repo, but that would discard the commit history of the inner repo. What I would like to do is replay the commits of the inner git repo onto the outer one. My desired end state is for all of the commits I have made to the submodule (but not the commits that were made before I forked it) to be reflected in identical commits to that subdirectory of the outer repo (i.e. a commit to src/lib.rs in the inner repo gets translated into a commit on submodule/src/lib.rs in the outer repo), and for that submodule to not be a git repo anymore and go back to being a regular old subdirectory. That is, my desired end state is what I would have if I had done rm -rf .git immediately after cloning the submodule's repo and continued my development from there.

I could do this by rolling the inner repo back to before I made the first change, moving the .git directory somewhere else, git adding that directory from the outer repo, moving the .git directory back, roll the inner repo forward one commit, rinse and repeat (I have a single digit number of commits in the inner repo, so that would theoretically be feasible), but I'm wondering if there's an easier way.

wallefan
  • 354
  • 1
  • 11

1 Answers1

-1
  1. Make sure you are on the main branch of the outer repo.

  2. Checkout the desired commit in the inner repo. You can do this by running git checkout <commit-hash>.

  3. Run the following command to replay the commits from the inner repo to the outer repo:

    git am <inner-repo-path>/.git/FETCH_HEAD

This will create new commits in the outer repo that are identical to the commits in the inner repo.

  1. Once all of the commits have been replayed, you can remove the .git directory from the submodule by running

  2. You can now commit the changes to the outer repo.

Here is an example of how to do this:

 # Make sure you are on the main branch of the outer repo.
git checkout main

# Checkout the desired commit in the inner repo.
git checkout <commit-hash>

# Replay the commits from the inner repo to the outer repo.
git am submodule/.git/FETCH_HEAD

# Remove the .git directory from the submodule.
rm -rf submodule/.git

# Commit the changes to the outer repo.
git commit -a

This will flatten the two repos into a single repo, and the commit history of the inner repo will be preserved in the outer repo.

Muhammad Idrees
  • 570
  • 4
  • 10
  • After checking out a previous commit my submodule does not have a FETCH_HEAD file. What should I do? – wallefan Jul 28 '23 at 01:00
  • you can: Navigate to the submodule directory. Fetch and checkout the correct submodule commit using git fetch and git checkout. Update the submodule in the main repository with git submodule update --remote. Commit the updated submodule reference. Push the changes – Muhammad Idrees Jul 28 '23 at 01:04