Reputation:
Is it possible to check out subdirectories of a repository in Git?
Imagine I am setting up a new WordPress installation. I will create two new directories for my plugin and theme customization:
wordpress/wp-content/plugins/myplugins/
wordpress/wp-content/themes/mytheme/
I want to maintain these directories via Git. In Subversion, I would accomplish this by having trunk/myplugins/
and trunk/mytheme/
directories and checking out subdirectories. Does Git have a way to accomplish the same task using a single repository?
I could just be missing the boat on some Git paradigm, as a long time SVN user with little exposure to Git.
Edit: Multiple branches storing different content is an interesting way to handle this.
Upvotes: 190
Views: 181198
Reputation: 384454
git clone --filter
+ git sparse-checkout
downloads only the required files
E.g., to clone only files in subdirectory small/
in this test repository: https://github.com/cirosantilli/test-git-partial-clone-big-small-no-bigtree
git clone -n --depth=1 --filter=tree:0 \
https://github.com/cirosantilli/test-git-partial-clone-big-small-no-bigtree
cd test-git-partial-clone-big-small-no-bigtree
git sparse-checkout set --no-cone /small
git checkout
This option was added together with an update to the remote protocol, and it truly prevents objects from being downloaded from the server.
I have covered this in more detail at: How do I clone a subdirectory only of a Git repository?
Tested on git 2.30.0 on January 2021.
Upvotes: 39
Reputation: 11
I looked at the different response including ones from How do I clone a subdirectory only of a Git repository?
They weren't very simple answers, so I decided to write a small shell script that should help simplify the process. See https://gist.github.com/hiranp/a26e334369386211709f4846929a6157
#!/bin/env bash
# This script clones the remote repository using the --filter=blob:none option to avoid downloading any file contents.
# It then checks out the specified remote branch and enables sparse-checkout. The sparse-checkout pattern is set to only
# include the desired folder, and finally, the latest changes are pulled from the remote branch.
# NOTE:
# Customize this script by setting the REMOTE_REPO_URL, REMOTE_BRANCH, GIT_FOLDER_PATH, and LOCAL_REPO_PATH variables.
# Set the remote repository URL
REMOTE_REPO_URL="<URL>"
# Set the remote branch name
REMOTE_BRANCH="branch-name"
# Set the path to the folder you want to copy
GIT_FOLDER_PATH="path/to/folder"
# Set the path to the local repository
LOCAL_REPO_PATH="path/to/local/repo"
echo "Cloning the remote repository...to ${LOCAL_REPO_PATH}"
if [ ! -d "${LOCAL_REPO_PATH}" ]; then
mkdir -p "${LOCAL_REPO_PATH}"
# Shadow clone the remote repository
git clone --depth 1 --no-checkout --filter=blob:none "${REMOTE_REPO_URL}" "${LOCAL_REPO_PATH}"
fi
# Change to the repository directory
cd "${LOCAL_REPO_PATH}"
# Checkout the remote branch
git checkout "${REMOTE_BRANCH}"
if [ ! -f ".git/info/sparse-checkout" ]; then
# Enable sparse-checkout
git sparse-checkout init
# Set the sparse-checkout pattern to only include the desired folder
git sparse-checkout set "${LOCAL_REPO_PATH}"
fi
# Pull the latest changes from the remote branch
git pull origin "${REMOTE_BRANCH}"
Upvotes: 1
Reputation: 8598
You can revert uncommitted changes only to particular file or directory:
git checkout [some_dir|file.txt]
Upvotes: 0
Reputation: 4725
There is an inspiration here. Just utilize shell regex
or git regex
.
git checkout commit_id */*.bat # *.bat in 1-depth subdir exclude current dir, shell regex
git checkout commit_id '*.bat' # *.bat in all subdir include current dir, git regex
Use quotation to escape shell regex interpretation and pass wildcards to git.
The first one is not recursive, only files in 1-depth subdir
. But the second one is recursive.
As for your situation, the following may be enough.
git checkout master */*/wp-content/*/*
git checkout master '*/wp-content/*'
Just hack the lines as required.
Upvotes: 1
Reputation: 15484
Sparse checkouts are now in Git 1.7.
Also see the question “Is it possible to do a sparse checkout without checking out the whole repository first?”.
Note that sparse checkouts still require you to download the whole repository, even though some of the files Git downloads won't end up in your working tree.
Upvotes: 136
Reputation: 18145
You can't checkout a single directory of a repository because the entire repository is handled by the single .git folder in the root of the project instead of subversion's myriad of .svn directories.
The problem with working on plugins in a single repository is that making a commit to, e.g., mytheme will increment the revision number for myplugin, so even in subversion it is better to use separate repositories.
The subversion paradigm for sub-projects is svn:externals which translates somewhat to submodules in git (but not exactly in case you've used svn:externals before.)
Upvotes: 1
Reputation: 118168
There is no real way to do that in git. And if you won’t be making changes that affect both trees at once as a single work unit, there is no good reason to use a single repository for both. I thought I would miss this Subversion feature, but I found that creating repositories has so little administrative mental overhead (simply due to the fact that repositories are stored right next to their working copy, rather than requiring me to explicitly pick some place outside of the working copy) that I got used to just making lots of small single-purpose repositories.
If you insist (or really need it), though, you could make a git repository with just mytheme
and myplugins
directories and symlink those from within the WordPress install.
MDCore wrote:
making a commit to, e.g., mytheme will increment the revision number for myplugin
Note that this is not a concern for git, if you do decide to put both directories in a single repository, because git does away entirely with the concept of monotonically increasing revision numbers of any form.
The sole criterion for what things to put together in a single repository in git is whether it constitutes a single unit, ie. in your case whether there are changes where it does not make sense to look at the edits in each directory in isolation. If you have changes where you need to edit files in both directories at once and the edits belong together, they should be one repository. If not, then don’t glom them together.
Git really really wants you to use separate repositories for separate entities.
Submodules do not address the desire to keep both directories in one repository, because they would actually enforce having a separate repository for each directory, which are then brought together in another repository using submodules. Worse, since the directories inside the WordPress install are not direct subdirectories of the same directory and are also part of a hierarchy with many other files, using the per-directory repositories as submodules in a unified repository would offer no benefit whatsoever, because the unified repository would not reflect any use case/need.
Upvotes: 18
Reputation: 214506
Actually, "narrow" or "partial" or "sparse" checkouts are under current, heavy development for Git. Note, you'll still have the full repository under .git
. So, the other two posts are current for the current state of Git but it looks like we will be able to do sparse checkouts eventually. Checkout the mailing lists if you're interested in more details -- they're changing rapidly.
Upvotes: 9
Reputation: 2238
One thing I don't like about sparse checkouts, is that if you want to checkout a subdirectory that is a few directories deep, your directory structure must contain all directories leading to it.
How I work around this is to clone the repo in a place that is not my workspace and then create a symbolic link in my workspace directory to the subdirectory in the repository. Git works like this quite nicely because things like git status will display the change files relative to your current working directory.
Upvotes: 16
Reputation: 27301
As your edit points out, you can use two separate branches to store the two separate directories. This does keep them both in the same repository, but you still can't have commits spanning both directory trees. If you have a change in one that requires a change in the other, you'll have to do those as two separate commits, and you open up the possibility that a pair of checkouts of the two directories can go out of sync.
If you want to treat the pair of directories as one unit, you can use 'wordpress/wp-content' as the root of your repo and use .gitignore file at the top level to ignore everything but the two subdirectories of interest. This is probably the most reasonable solution at this point.
Sparse checkouts have been allegedly coming for two years now, but there's still no sign of them in the git development repo, nor any indication that the necessary changes will ever arrive there. I wouldn't count on them.
Upvotes: 1