1

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

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.

11
  • 1
    Aren't you kind of re-inventing branching? Commented May 23, 2017 at 19:24
  • Dir structure is more or less out of my control to some extent for the code base I'm currently working on, but the reasoning behind it sort of makes sense. Traditional branching is used for some cases, while variant directories with a specialized build system is used to cut down on the redundancy while targeting simultaneously multiple targets... I'm kind of locked in, though, but yea, I get it's a weird case in some regards, but that's why I listed the simple version first as I feel the general question of how to cp -rf only version controlled content is pretty ubiquitous. Commented May 23, 2017 at 19:30
  • I wouldn't expect any tool other than git (or one specifically designed to work with a Git repository) to be able to do this. Commented May 23, 2017 at 19:30
  • 3
    Why not use the git archive command to do this? It can pipe a tarball to its stdout, so pipe that to tar -x -C /path/to/destination and you're done. Commented May 23, 2017 at 19:34
  • 2
    stackoverflow.com/questions/15606955/… has commands to show all tracked files. Commented May 23, 2017 at 19:36

4 Answers 4

4

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

Sign up to request clarification or add additional context in comments.

Comments

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

2 Comments

tar Tc and tar Cx don't work for me. What are the T and C supposed to do?
Akk. Dropped an argument. T is "read a list of files", C is "change directory"
1

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.

2 Comments

This seems like a good answer, but I'm on Mintty BASH which doesn't have rsync built in... is there an equivalent w/out it that you'd propose? BASH version is GNU bash, version 4.3.46(2)-release; Mintty version is 2.0.3
If this is on Windows, you may not have working process substitutions either. I don't remember if it extends to msys builds, but that functionality was broken on cycgwin builds of bash for a long, long time.
1

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

mkdir variantB cd variantA cp --parents $(git ls-files .) ../variantB 

1 Comment

This will fail badly if any filenames contain spaces or could be interpreted as globs. Much safer to use git ls-files -z . and parse the NUL-delimited stream this emits.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.