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.
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.
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.
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
.
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.
⚠️ Disclaimer! This is how I use submodules to deploy onlydist/
directory to live server. Other developer might use other methods e.g distribution branch etc.
- 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
andWebpack
so the auto-generateddist/
directory is always there. Do NOT use yourdist/
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
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!