In BASH How Can One Recursively Copy Only Content Version-Controlled by Git?

Is there a way to in a bash terminal mass recursive copy (cp -rf equivalent) from one subdirectory to another, preserving the directory structure but ONLY copying files/directories are version controlled by git?

The naive strategy would be…

  • Python Git Bash CMD Script
  • GIT, check return code of commands (bash script)
  • thinnest way to create a remote branch
  • fatal: Authentication failed Git Bash
  • Git Branch Bash Variable Shortcut
  • How to change git account in Git bash?
  • cp -rf <targetDir> variantDir/; git add variantDir;
    

    … but that would pick up a bunch of files that are data files dropped by the IDE, etc. I’m working with — files which aren’t currently version controlled in <targetDir>.


    As background, I have the following directory structure:

    ${baseDir}/
    ${baseDir}/variantA
    

    Basically, ${baseDir}/ contains all the original version controlled content, and ${baseDir}/variantA contains the content that differs for a targeted variant. For the original target folder ${baseDir}/variantA is ignored. For the second target the folder variantA is included, effectively superceding any content that matches other than the variantA part of the path.

    i.e. if I had:

    ${baseDir}/someFolder/someStuff.cpp
    ${baseDir}/variantA/someFolder/someStuff.cpp
    

    It would use the second path for the second target, and the first path for the first target.

    Now I’m adding variantBand it’s a full variant (unlike variantA that has some fall through to the base target’s files). It’s closer to variantA so I want to copy over everything in the base folder first, THEN copy over everything in the variantA subfolder to the variantB subfolder … that way I have a new copy of all content (including the fall throughs) to version commit, with a preference for variantA file versions, when such overlap exists.

    I would use the naive snippet above, but I have a more fundamental problem that my IDE has dropped *.xml files and other junk that git is somehow smart enough to flag as not uncommitted content (probably language specific), but which cp would ignore. I believe git would copy these files if EXPLICITLY asked to do, so, hence I want to exclude all the non-git controlled content, while using the general strategy stated prior.

  • Bash shell script error sh: '
  • Cygwin's Git command is removing the curly braces from a command. How do I prevent this?
  • How to run bash script after git push
  • Ignoring files in a directory, but not the directory itself, in Git
  • Disable `git push --force` locally
  • git scripting: How to list all git branches containing a commit
  • 4 Solutions collect form web for “In BASH How Can One Recursively Copy Only Content Version-Controlled by Git?”

    git ls-files | tar Tc - | tar Cx /path/to/destination
    

    this takes what’s at your current spot in the worktree. To use a subdirectory of a commit use the git archive solution @Charles Duffy mentioned,

    git archive master:path/to/subdir | tar Cx /path/to/destination
    

    and to work with your currently-added-but-not-committed content you can subsitute $(git write-tree) for master above.

    Why not just create a fresh work tree (which obviously will only have source-controlled files in it since it’s generated from source control)?

    There are a few ways to do this (with clones or work trees) but I would think

    git worktree add -b variantB path/where/variantB/will/be/created master 
    

    would get you started with the relevant files (those under source control). Then you just copy path/where/variant/B/will/be/created/variantA over path/where/variant/B/will/be/created

    And because of the -b option, you’re already on a fresh branch – which is how you’ll likely want to maintain this variation anyway since it’s a full replacement. (For varientA it’s debatable since you want variantA to keep up-to-date with baseline changes to the files it doesn’t vary…)

    But if you don’t want to maintain a new branch for this, no big deal. Use either a clone or a worktree add command (without -b) to create a worktree image of variantB as above, but then move the resulting files into your default worktree under a …/variantB directory

    To copy only files under source control from variantA to variantB:

    cd variantA
    rsync --files-from <(git ls-files .) . ../variantB
    

    This works by including only files returned by git ls-files.

    Edit: the <() syntax is called process substitution.

    If you have GNU cp, you can use the --parents option:

    mkdir variantB
    cd variantA
    cp --parents $(git ls-files .) ../variantB
    
    Git Baby is a git and github fan, let's start git clone.