1

Ok so git diff --name-status branch1..branch2 gives me this:

M       .gitignore
M       README.md
A       README_orig.md
M       deps/npm/lib/cache/add-remote-git.js
M       deps/npm/lib/fetch-package-metadata.js
M       deps/npm/lib/utils/git.js
A       git_wrapper.bat

I would like to copy those files (not patches produced by git diff) from branch2 into a brand new orphan (empty) branch and commit there.

Is there some succint way of achieving that with git or it's git checkout branch2 -- path1, ...path2, etc. time?

UPDATE

OK, I see I should have explained the context.

This is a rather unusual situation. I'm packaging some elaborate software for the company (namely, node.js, Visual Studio Code) for the company. This requires a number of customizations reproduced to the same effect on each new version incoming.

Now, the way e.g. Visual Studio Code compilation works is that it does not ship entire source code needed to compile the app. It downloads tons and tons of stuff during compilation process (clones literally dozens or even hundreds of Github repos), regenerates some stuff, etc, while we need to apply our customizations and fixes on top of that downloaded stuff; maintainers of some node modules are unresponsive re fixing the stuff or even applying fixes I worked out and dutifully reported, but we still need to apply those fixes and for obvious reasons do not want to create our forks of those modules that will get stale in time.

I obviously cannot just use usual git diff to apply our specific fixes and customizations between say v1.12.2acme and v1.14.0asshippedbymicrosoft because lots of unrelated files change. Now I guess I could store the changes only in the past fixed/customized branches or tags of the product:

git diff commit_acme_fixed..commit_v1.14.0 -- file1.js
git diff commit_acme_fixed..commit_v1.14.0 -- file2.js

but... I do not really want to do that. My idea is to have unrelated (orphan) acme_customisations branch where all the files with their versions are stored only for purpose of patching subsequent incoming new releases. I could then create a commit or tag in that branch that will encapsulate cleanly just the changes I need replayed on incoming v1.14.0asshippedbymicrosoft release as our customizations will surely have to be modified sooner or later given the changes in the base product.

It's also much, much easier for other people to figure out just what files are changed by us among plethora of files changing between versions of the base product (I guess you could find that out by committer, but that's additional filtering at this level you have to do and also the risk you'll miss someone as a committer and lose some changes from sight, while working with 2 branches/tags/commits is much more natural and well-supported by git).

LetMeSOThat4U
  • 6,470
  • 10
  • 53
  • 93

2 Answers2

0

Do you mean that you want only those files in the new commit?

It's unwise, I think, to think about "files in a branch"; Git's branches are too flexible; what you have are commits, and each commit has some parent commits and a complete snapshot, not a patch. But in the orphan branch state there, is no current commit. The current branch is just a name and a notion, not an actual branch. This is also the state you have in a newly initialized, completely empty repository: the current branch is just a name, master; master does not actually exist yet.

When in this state, git commit will create a commit as usual, but this new commit will have no parent commit—it's a new root commit—and only then will the new branch exist, pointing to this new commit. Whatever files are in the index when you run git commit, those files will be in the new commit, as always. So just remove all unwanted files from the index, and put the desired version of those files into the index. The contents of the work-tree are irrelevant, but in the steps below, we will have them come along for the ride.

git status  # make sure there's nothing to commit

(we're going to wreck any unsaved work—untracked files are mostly safe, but it's wise not to depend on it).

Now I think the simplest way to do this is to go ahead and set up to make the new branch with its new single root commit:

cd $(git rev-parse --show-toplevel)   # if not already there
git checkout --orphan newbr           # prepare to create new branch

Now empty out the index:

git read-tree --reset -u $(git hash-object -t tree --stdin </dev/null)

This computes the hash ID of the empty tree and uses that as the index contents. Corresponding work-tree files get deleted (-u), leaving only untracked files (which you may remove or leave in place as desired).

Then, use just what you suggested, git checkout branch2 --, but with diff piped to xargs to automate it:

git diff --name-only -z branch1 branch2 | xargs -0 git checkout branch2 --

and now you are ready to commit ... and the only files in this new commit will be the ones added to the index-and-work-tree by git checkout branch2 --.

(You might want to add --diff-filter=AM to your git diff, so as to get only A and M files. Also if you are doing this repeatedly and automatedly, consider using git diff-tree, a plumbing command, rather than git diff, which as a user-facing porcelain command can change its behavior depending on user settings, which vary from one person to another or over time.)

torek
  • 448,244
  • 59
  • 642
  • 775
0

The volume of repos being wrangled and VSC doing it all under the covers hides the fact that what you've got here is a perfectly ordinary git workflow: you're maintaining the acme branch on a vendor base.

You've got an acme branch with your work, you rebase that on new vendor releases as they arrive, fix up anything that needs fixing, tag the publication commit, people who want your version can pull your release tags, possibly on top of their already-acquired vendor bases.

For any one repo that VSC has already cloned,

git remote add acme u://r/l
git fetch acme v1.14acme
git checkout v1.14acme

and you're done. The fetch got only what's different, exactly as you desire, and to boot it shows the structure of the history you're publishing, not just the orphaned tip with no way to automate tying it to a particular vendor base.

edit: really, for one-off fetches like this, you can dispense with adding the remote:

git fetch u://r/l v1.14acme
git checkout v1.14acme
jthill
  • 55,082
  • 5
  • 77
  • 137