简体   繁体   中英

Git: How to apply changes of one file from branch A to another file in branch B of the same repo?

In repository X, I have a branch A in which I would like to apply changes from branch B. I'd like to apply the sum of all the changes of a file d (with master as a base) from branch B onto a different file c on branch A (which also has master as a base):

Branch A (based on master)

app/file_c (should receive the changes from app/file_d on branch B)
app/file_d


Branch B (based on master)

app/file_c
app/file_d (has the changes I'm interested in)

I tried moving the file d on branch B to the location of file c and then cherry-picking the changes as a commit off branch B when on branch A, but this will overwrite the entire file and make the git history less readable.

Which git commands can help me to achieve this update?

Remember these items:

  • Commits hold snapshots. They're not changes , they are full copies of each file. (Git does automatic de-duplication, so the fact that many commits re-use many files means that they really re-use the file, even though each has a full snapshot. All of them have one snapshot of that file, shared across all of the snapshots that have that one version of that file.)

  • Branch names merely identify one particular commit. Each commit refers back to one earlier, or parent , commit. 1 The commit that the branch picks out is, by definition, the last commit in that branch. (This in turns means that some commits are on many, or even every, branch simultaneously.)

  • Hence, to get changes , you have to pick two commits and compare them . Otherwise all you have is one snapshot. (Try out the link and compare the first pair of pictures. Did the time on the clock change?)

  • Commits themselves are completely read-only. You can never change anything inside any existing commit. What Git has you do is extract the existing commit to a work area. You can make any changes you like in this work area, then use git add and git commit to make a new commit. The new commit adds to the repository; the existing commits remain there, unchanged.


1 A merge commit refers back to more than one previous commit, and at least one commit in every repository—the very first one—literally can't refer back to a previous commit, so it doesn't. But most commits mostly have one parent, which is the previous commit. That's what history is , in a Git repository: this backwards-pointing chain of commits, going from commit, to parent, to grandparent, and so on. Each of these commits has a full snapshot of all of the files.


I have a branch A in which I would like to apply changes from branch B.

Let's draw this, with earlier commits towards the left and later ones towards the right. We'll use single uppercase letters to stand in for real Git commit hash IDs, but reserve A and B for branch names:

       I--J   <-- A
      /
...--G--H   <-- master
         \
          K--L   <-- B

Of course, I don't know what your branches look like, but here, we have three branches:

  • A ends at commit J ;
  • B ends at commit L ; and
  • master ends at commit H .

The parent of commit J is commit I , whose parent is commit G . The parent of commit L is commit K , and K 's parent is commit H , whose parent is commit G as well. This means commit G is on all three branches; commit H is on master and B ; and commits IJ and KL are only on A and B , respectively.

I'd like to apply the sum of all the changes of a file d (with master as a base) from branch B onto a different file c on branch A (which also has master as a base)...

OK, perhaps I should have drawn this as:

          I--J   <-- A
         /
...--G--H   <-- master
         \
          K--L   <-- B

Your goal is now to find changes to some file d , as seen in a commit—probably H , as identified by the name master —to the version of that file as seen in commit L , identified by branch name B .

The git diff command does this sort of thing:

git diff master B

will show you what changed in all the files in commit H (found using the name master ) to all files in commit L (found using the name B ), as if in a game of spot-the-difference. You can have Git trim back to just showing whatever is different in file d , disregarding all other files, by asking git diff to limits its output:

git diff master B -- d

You might want to redirect the git diff output to a temporary file:

git diff master B -- d > /tmp/the_diff

Note that this file has, in its instructions, the file name d along with the changes to make. You will need to replace this file name , perhaps using any text editor on the saved git diff output, as we'll see in a moment.

onto a different file c on branch A

Remember that a branch name just identifies some particular commit. To get that commit out of Git, into your work area, use:

git checkout A

or (in Git since 2.23):

git switch A

(these particular invocations do exactly the same thing; the new git switch command is just more user-friendly than the old git checkout command, because many git checkout operations that will silently overwrite your work without asking have been moved out to a separate command, so that git switch is safer to use).

Your work-tree now has a modifiable copy of file c . You now need to modify it, using the instructions that Git produced for how to modify the copy of d that is in commit H to make it look like the copy that is in commit L . To do this, you will first change the instructions to refer to file c instead of file d . Then you can run:

git apply /tmp/the_diff

which will try to modify the (now modified to say "change file c ", not "change file d ") diff to the copy of c that is in your work-tree.

If all goes well, Git will have applied all the changes. If some of them do not apply correctly, you have various options, such as --reject or --3way .

Note that by default, git apply will not stage the changes to be committed; it leaves that to you. Using git apply --index will change this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM