Checkout subdirectories in Git?
Asked Answered
B

10

185

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.

Buoyant answered 7/10, 2008 at 19:47 Comment(5)
why don't you checkout the whole repo, and make a symbolic link to the subdirectories you want to work with?Gaseous
possible duplicate of Is there any way to clone a git repository's sub-directory only?Retortion
Simple answer here.Smashandgrab
Is it possible to do sparse checkout and reference Git repository?Lava
Related: stackoverflow.com/questions/2336580/…Kealey
C
136

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.

Crossopterygian answered 7/10, 2008 at 19:47 Comment(2)
And is there a way to rename those folders? If I sparse checkout /foo/bar/foobar, is it possible to see it only as /foobar in my local repository?Ensue
Git documentation seems to be unique in being completely pointless. Case in point that Sparse checkouts link which doesn't remotely describe what sparse checkouts are, or what they are for.Sociology
A
31

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.

Abbey answered 11/9, 2018 at 7:19 Comment(0)
S
18

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

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.

Saker answered 7/10, 2008 at 20:6 Comment(0)
D
16

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.

Disenthral answered 22/3, 2011 at 16:33 Comment(2)
This only works in an OS supporting symbolic links. They need to change the way sparse checkouts works.Ability
+1 for the idea with a symbolic link on the checked out directory. However, a sparse checkout and a symbolic link are not mutually exclusive: you don't need a full fledged clone.Decembrist
P
10

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.

Peal answered 8/10, 2008 at 20:17 Comment(1)
Good to know! I do like having such closely-related directories under one repository, and would do it if at all possible.Buoyant
P
1

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.)

Poppied answered 7/10, 2008 at 20:20 Comment(0)
M
1

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.

Mcfall answered 16/1, 2010 at 3:13 Comment(0)
Q
1

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.

Quadri answered 16/9, 2019 at 14:51 Comment(0)
M
1

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}"
Mighell answered 17/4, 2023 at 15:50 Comment(0)
A
0

You can revert uncommitted changes only to particular file or directory:

git checkout [some_dir|file.txt]
Allinclusive answered 28/12, 2019 at 17:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.