Search code examples
gitdevopsgit-log

Git: Copy a Group files from one Git Branch into Another Git branch?


How can I Copy all the files, I have changed in last 10 days, from one branch, and place them into another git branch?

This locates all the files changed,

https://stackoverflow.com/a/8016702/14432516

git log --since="10 day ago" --author="John Smith" --name-only --pretty=format: | sort | uniq

Now, trying to utilize answer below, to specify a Subset of files to copy into another git branch. This answer takes All the files, not a specified group.

How can I get All the files from one git branch, and put them into the current branch without merging?

Example: Move/Copy 25 files from Git Branch A to Git Branch B, taking the files as is, Or merge (take source). Want all the files as in Branch A.

Looking for a quick automated way to conduct this for 100+ files.


Solution

  • Files aren't actually in branches. Files are in commits. Every commit has a full and complete copy of every file, or more precisely, every file that Git knew about at the time you (or whoever) made that commit.

    Why this matters is that it makes the answer easy—or rather, mostly easy. We'll see more about this mostly part in a bit.

    Given:

    This locates all the files [I want to copy]:

    git log arguments --name-only | sort -u

    you can now simply copy those files from the last commit in the sequence of commits that git log examined. Since the specified arguments above didn't include any specific commit, Git started from the commit that was the HEAD commit at that time, which would be the tip commit of the branch you had checked out at that time. For instance, let's say you had branch branch-A out.

    You might also want to, at this point, do: arguments="--since="10 day ago" --author="John Smith" --name-only --pretty=format:" files=$(git log $arguments | sort -u) echo $files

    (The only reason for setting $arguments first is to make all this fit on one line in this posting, and/or to let you run git log like this more than once.)

    [How do I get (those files) ready to be committed into a new commit I will make on branch-B?]

    You'd next run:

    git checkout branch-B
    

    as usual. Then, as in the better answer to the question you linked, you would use:

    git checkout branch-A -- $files
    

    The $files variable is set from the output from the git log --name-only | sort -u command.

    Mostly

    Where the mostly comes in is that $files can have two problems:

    1. Some of the file names might contain spaces. Shells such as bash—the examples here are all sh / dash / bash compatible—treat spaces as breaking up arguments, which is why you can run git log --since=... in the first place: each of those log, --since, --name-only, and so on have to be passed to the Git command as separate arguments.

      But suppose that you have a file named path/to/file with spaces.ext. Then $files contains path/to/file with spaces.ext, which to bash, looks like you typed in path/to/file as one argument, with as a second argument, and spaces.ext as a third argument.

      If you are stuck with this situation, bash in particular has some ways to deal with it. Your best bet is to avoid spaces in file names in the first place, though.

    2. It's possible that one of the activities on some file, that git log showed you above, is that the file got deleted in that commit. The --name-only option tells Git to show you only the names of files that had some change made, between the parent of the commit and the commit in question. If the change-made was "delete the file entirely", that file isn't in the new commit, and probably isn't in the final commit either, because it probably stayed deleted.

      The right thing to do for such a file is generally to leave it deleted. So if you get an error about this file, just take it out of the $files set: it's not in the commit at the tip of branch_A so you can't copy it out of that commit, but you don't want to copy it out anyway. Just leave it deleted.

    Note that instead of git checkout above, you can use git restore if your Git is 2.23 or later.1 This isn't any better than git checkout, it's just a little clearer: the git checkout command in Git versions predating 2.23 can do one of several jobs, and in Git 2.23, those jobs were subdivided into two separate commands: git switch does about half of them, and git restore does the other roughly-half of those jobs.

    (Having split the command into two, the split-up commands were then made smarter and better. So in the current versions of Git, as time goes on, git switch and git restore put together can do more than git checkout. The old git checkout continues to exist so that if you are used to it, you can keep using it. If you need some of the new abilities, though, you must switch to the new commands.)


    1When using git restore, you'll want git restore --source=branch_A -iw -- $files, rather than git checkout branch_A -- $files. This is a little longer to type in, which can be annoying, but it has the same effect. The "smarter-ness" of git restore is that you can select whether the files go into Git's index or not, and whether they go into your working tree or not, individually.