5

I have a parent_repo and a sub_repo within it, like this:

.\parent
.\parent\parent_file.html
.\parent\.gitignore
.\parent\lib\sub_repo
.\parent\lib\sub_repo\sub_file.html
.\parent\lib\sub_repo\.gitignore

Before I know about subtree or submodule, I use git for both of the repos. But when people see the parent_repo, they cannot see the sub_repo 's code.

enter image description here

It is suggested that, I should use subtree or submodule. What does a grey icon in remote GitHub mean

Then What should I do? To make the sub_repo code available in parent_repo?


I looked at the docs of submodule and subtree. It seems like subtree is better than submodule. But the tutorial are not very illustrative (https://medium.com/@v/git-subtrees-a-tutorial-6ff568381844#.aztjizd8g).

I run git subtree add —-prefix=lib/sub_repo my-subtree master, and it reports prefix lib/sub_repo already exists.

Is there any simpler example that I can follow?

Dharman
  • 30,962
  • 25
  • 85
  • 135
ZK Zhao
  • 19,885
  • 47
  • 132
  • 206

3 Answers3

3

First let's explain what is the main difference between subtree and submodule:

both of them are used for having another repo inside existing repo. The main difference is that git submodule is independent self-contained repository while subtree store the date in the parent (original) repo.


Now let's dig in and explain in more details:

Is there any simpler example that I can follow?

Submodule is a standalone git project so the code will be checked out to a new folder under the root folder and it's not part of your master branch.

Your root folder will contain a submodule file and you will have to init && update it on every clone you make.

# Add the desired submodule to your code base
git submodule add <url>

You must run two commands:

git submodule init 

to initialize your local configuration file, and

git submodule update 

to fetch all the data from that project and check out the appropriate commit listed in your superproject:

So the full script is this:

git submodule add <url>
git submodule init
git submodule update

You simply need to be in your root folder and then add the submodule folder.

git submodule add <url>

Now when you clone the project you simply need to init and update the submodule

git submodule init
git submodule update

Git 1.8.2 features a new option --remote

git submodule update --remote --merge

will fetch the latest changes from upstream in each submodule, merge them in, and check out the latest revision of the submodule.

enter image description here


git subtree

Git subtree allows you to insert any repository as a sub-directory of another one

Very similar to submodule but the main difference is where your code is managed. In submodules the content is placed inside a separate repo and is managed there which allow you to clone it to many other repos as well.

subtree is managing the content as part of the root project and not in a separate project.

Instead of writing down how to set it up and to understand how to use it you can simply read this excellent post which will explain it all.

https://developer.atlassian.com/blog/2015/05/the-power-of-git-subtree/

CodeWizard
  • 128,036
  • 21
  • 144
  • 167
  • Thanks. I now deleted the repo, and then re-add it through `subtree`. Still experimenting with this feature. – ZK Zhao Apr 19 '16 at 14:25
  • But do the mapping in the subtree no longer works when the parent repo is cloned again and can't be used to pull in new updates or push upstream code changes back to the child or subtree repo, is that correct? – Richa Dua Jun 10 '20 at 20:47
1

Thought I'd provide a more specific answer related to subtree approach. This was my preferred method because I needed to be able to push changes to the sub repo from the parent repo.

We just have to create the parent repo, remove the existing repo, then add the existing repo back to the parent repo as a subtree. Your local directory structure won't change and neither will any of your namespacing between modules.

Below assumes there is an existing repo on your local machine inside a folder existing/ and you want to nest it inside a new parent repo in the folder parent/.

So the directory tree should look something like this:

parent/
 |
 +-- existing/
 |     |
 |     +-- .git/
 |     |
 |     ...
 ...

Process:

  1. !!! Commit any changes to the existing repo !!! Otherwise, you will lose them.

  2. Create new repo in parent/ folder called parent

    • you’ll get a warning related to the nested existing repo; ignore it
  3. Copy the existing folder to another location. You'll need it to copy back any files that are .gitignored

  4. Remove the existing/ folder with rm -rf existing and commit

    • Now any commits will be tracked in parent repo
  5. Add the existing repo back as a subtree to parent:

    git remote add existing https://github.com/username/existing.git

    git subtree add --prefix=existing/ existing master

  6. Copy back any files that were ignored and therefore not in the existing repo but necessary for your project

Thats it. Now you can push any changes to existing repo separately via git subtree add

Example:

In the folder existing/,

echo "a new text file in existing repo" >> newtextfile.txt
git add .
git commit -m "add new file to existing repo"
cd ../ # to parent folder
git subtree push --prefix=existing/ existing master

Remember changes to the parent repo also have to pushed separately and that changes to existing will be included in in the parent repo.

Ryan Skene
  • 864
  • 1
  • 12
  • 29
1

I know this is an old question but to help those who are facing the problem right now here's how to fix it, simply and without having to re-download the repo:

  1. Rename the existing folder (eg. subtree-dir to subtree-dir_temp.

    mv original/subdir/path new/path/for/temp/dir`
    
  2. Commit

  3. Run Git Subtree add with the renamed folder as the remote.

    git subtree add --prefix=original/subdir/path path/to/renamed/folder master --squash
    

    As you can see we are entering the renamed folder path where the remote url would go, git doesn't care about this and is able to "pull" it.

  4. Commit

  5. Remove/delete the folder that was renamed as it has been already copied successfully by Git to the correct path.

    rm -r path/to/renamed/folder
    
  6. Commit

The reason why you should commit in between changes is because Git creates commits automatically whenever a subtree is added, and therefore, it will complain if there are uncommited changes (for your own safety).