Skip to content

Instantly share code, notes, and snippets.

@0xkhan
Last active November 14, 2022 17:50
Show Gist options
  • Save 0xkhan/8fb65ca3d01020e7aa65b1dbb75cfb10 to your computer and use it in GitHub Desktop.
Save 0xkhan/8fb65ca3d01020e7aa65b1dbb75cfb10 to your computer and use it in GitHub Desktop.
Git Submodules

Push Only dist/ Folder To Live Server

I have googled many things and explored many questions on Stack Overflow to find the best way to push only dist/ directory to live server. I couldn't find any good solution but one person on Stackoverflow suggested to look at Git Submodules and this is the best solution I could come up with. I have never heard of submodules before because almost nobody uses them. If you never heard of it too then I'll first explain what a submodule is and then I'll show you how you can use submodules to push only dist/ directory to a live server.

What is a Submodule?

A submodule is a repository inside another repository. Basically a project within a project. For example we have a set of dependencies that we need for more than one project. We can put all those dependencies in a repo and add that repo as a submodule in each project. So we bring that repo into our project repo, it lives there and we have access to the whole repository.

Why and when would you use it?

The best example for when you should use submodules is sharing libraries. Other example might be when you have a repository and some files are used across multiple technologies what you can do is to add it as a submodule across all technologies that way every technology will have it available.

So how do you add a submodule?

Let's suppose we have two repos MyRepo and MySubRepo. MyRepo is our working repo, our project and MySubRepo is our submodule. Both are separate independent repositories. Now to add a submodule into your working repository Git has a command for it:

git submodule add <url>

In our case we wanna go inside MyRepo/ and run the following command:

/MyRepo$ git submodule add https://github.com/<username>/MySubRepo

Now MySubRepo is added as a submodule to our working repo MyRepo. If MyRepo had only README.md file it will now have MySubRepo as well:

/MyRepo$ ls
README.md .gitmodules MySubRepo

The .gitmodules is the file that keeps track of submodules. Next we wanna add the new added files and push them to our remote respository.

/MyRepo$ git status
new file: .gitmodules
new file: MySubRepo

/MyRepo$ git add .
/MyRepo$ git commit -m "Add submodule"
/MyRepo$ git push

The life cycle of submodules works differently from our standard repository. By default when we use git commands it will ignore all submodules unless we explicitly tell it. So basically if you do git clone it will ignore submodules also if you do git push or git branch it will also by default ignore submodules.

To explicitly tell it to focus on submodule we can use --recurse-submodules flag e.g:

git pull --recurse-submodules

# For cloning a repo
git clone <url> --recurse-submodules

If you forgot to use the --recurse-submodules flag you can alternatively use the following command after the repository is cloned:

git submodule update --init

If we go inside MySubRepo you will see that it's on a different branch and that's because we are in a different repository. You can manually checkout to master branch by running git checkout master.

How it works with changes

If we make changes to MySubRepo/README.md file and push the changes it will update MySubRepo repository but when we go back to MyRepo we can see that the branch is dirty that means there are changes. It will look like following:

/MyRepo$ git status

On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
    (use "git restore <file>..." to discard changes in working directory)
        modified: MySubRepo (new commits)

no changes added to commit (use "git add" and/or "git commit -a")

As it shows above the main repo is up to date but there were changes in our submodule that needs to be committed. Those changes are not actually changes in our main repo but it's the last commit hash inside our submodule. What that means is that the submodule is updated to a newer version and that specific commit has to be realised and committed inside our main repo. When we do that our submodule will point to the last commit. To update those changes in our working repo we do the following:

/MyRepo$ git add .
/MyRepo$ git commit -m "Update submodule version"
/MyRepo$ git push

Now those changes are also committed and both repos are up to date.

How to use submodules to only deploy your dist/ directory to live server

⚠️ Disclaimer! This is how I use submodules to deploy only dist/ directory to live server. Other developer might use other methods e.g distribution branch etc.

I use the following steps

  • Create a working repo which is the main project repo
  • Add your distribution folder as submodule. I name my submodule as <project name>-dist
  • Create your build. The build process has to be done carefully I use npm and Webpack so the auto-generated dist/ directory is always there. Do NOT use your dist/ directory or your submodule as distribution output in webpack because each time you make a build it'll wipe everything out inculding git files. Git files gone means your directory is not a repository anymore, therefore not a submodule.
  • Commit and push the changes in submodule
  • Go back to your main repo and commit the hash

How do I make a build?

My way of making a build is that I run npm run build and it takes care of everything. My build command first bundles everything and then copy the files from dist/ directory to <project name>-dist/ directory. This way only the changes are detected in submodule those changes can then be committed and pushed.

Build command in my package.json:

"scripts": {
    "devServer": "webpack serve",
    "watch": "webpack -w",
    "bundle": "webpack",
    "copy": "cp -r dist/* <project name>-dist/",
    "start": "npm-run-all --parallel devServer watch",
    "build": "npm-run-all bundle copy"
}

npm-run-all is a npm package that helps you run multiple commands when you do npm run <your command>. Of course you wanna ignore your dist/ folder as you ignore node_modules etc. You only push src/ in main Repo and your distribution is added as submodule so it's an independent repo which is only used for deployment.

To see how it looks check this repo out. It's just a static website which is deployed from a submodule. You'll not be able to see the submodule because it's a private repo. Yes, you can use a private repo as submodule.

I hope the information is presented clearly. If you have any questions or suggestions then comment them below!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment