0

Problem

I have 3 branches, main, dev, and feature. Both feature and dev have modified file myFile. I am currently on the feature branch with a clean working tree, and I want to merge dev into feature. However, there are merge conflicts in myFile.

I want to combine the changes to myFile from dev and feature and end up with conflict markers in the file.

Setup

Here are the steps to follow to set this situation up.

  1. mkdir test create a repo
  2. cd test
  3. git init
  4. git checkout -b main checkout the main branch
  5. touch myFile check in a file
  6. git add myFile
  7. git commit -m "add myFile"
  8. git checkout -b dev create a dev branch
  9. echo 'aaa' > myFile modify the file
  10. git commit -am "change myFile"
  11. touch fileB do some other changes
  12. git add fileB
  13. git commit -m "add fileB"
  14. git checkout -b feature main create a feature branch
  15. echo 'bbb' > myFile modify the file differently
  16. git commit -am "change myFile"
  17. touch fileC do some other changes
  18. git add fileC
  19. git commit -m "add fileC"

Now everything is set up and your history looks like this:

gitk

It's time to pull the changes in from dev into feature for myFile only, leaving the rest of the file tree untouched.

  1. git merge dev on no, conflicts!
  2. git status
On branch feature
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Changes to be committed:
        new file:   fileB

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   myFile

The goal is to change the contents of myFile to

<<<<<<< HEAD
bbb
=======
aaa
>>>>>>> dev

without modifying any other files at all.

How do you do that? Look at the "working but not nice solutions" below to see some methods for how to achieve this. But the point of this question is to discover if there is an easier or simpler way to do it, because I don't think the ways I listed below are very simple.

Failed Solutions

git checkout dev -- myFile

This just gives me the exact file that is in the dev branch, removing any changes that have been made on the feature branch.

git diff + git apply (reference)

  1. git diff feature dev -- myFile > temp.patch.
  2. git apply temp.patch.
  3. rm temp.patch

This also ends up with a file that has no conflict markers in it.

Bonus note: if the order is wrong (i.e. git diff dev feature -- myFile > temp.patch) then it won't work at all.

git cherry-pick

This is often recommended for this kind of fix, but there's no commit to cherry pick here. I need a file, not a commit. There could be dozens or hundreds of commits that touched myFile.

Working but not nice solutions

😖 New branch + copy file + merge

  1. git checkout main
  2. git branch deleteme
  3. git checkout deleteme
  4. git checkout dev -- myFile
  5. git commit -m "grab myFile from dev"
  6. git checkout feature
  7. git merge deleteme

(Then later on remember to git branch -D deleteme)

This is kind of terrible and muddies up the history. Plus you have to create a temporary branch.

😑 3-way merge with common ancestor

  1. git show dev:myFile > dev-version
  2. git show main:myFile > main-version
  3. git merge-file myFile main-version dev-version
  4. rm dev-version main-version

This is slightly shorter than the above method, but requires temporary files.

😨 Merge the branches and then reset or revert everything else

I don't even want to think about doing this.

Question

Is there any way to do this that doesn't involve creating temporary files/branches?

Potential duplicate question with no answer

9
  • Your second working solution seems like exactly answering what you want to do. You could put it in a script which also deletes the temporary files if that makes you feel better Commented Jan 12, 2024 at 1:03
  • It's not clear to me what you're asking for, nor what its purpose is. Do you want to merge dev and feature, but only myFile? Do you plan to commit it? Do you want only look at the conflicts for one file? Commented Jan 12, 2024 at 3:04
  • I think that duplicate answers your question. If it doesn't, leave a comment about how your question is different and I'll reopen. Commented Jan 12, 2024 at 3:25
  • That question is very similar, yes, but the answers there don't work for this question. The top answer there requires an interactive patch, which doesn't accomplish my goal of leaving the conflict markers in place. The next few answers describe other methods already discussed here. But in any event, I think I'm satisfied with the answer of "there isn't really a better way to do this; sorry." Commented Jan 15, 2024 at 17:48
  • @nullromo It remains unclear to me what you're asking for. Could you give a concrete example? git init a repository, show us the commands to get into your situation, and then show us the result you'd like. Also, please explain the purpose, that might help us understand. Commented Jan 15, 2024 at 22:06

2 Answers 2

0

Point 1

Since you are only bringing in changes for a single file you very likely do NOT want to create a merge commit for this. A merge commit in git means that ABSOLUTELY EVERYTHING from the source branch is now considered resolved and adapted into the destination branch. Once you have created a merge commit you cannot merge the history from before that again later on.

Point 2

Plus you have to create a temporary branch.

Is there any way to do this that doesn't involve creating temporary files/branches?

No. If you're not constantly creating temporary branches all the time you are doing git wrong!

Creating temporary branches or temporary commits are not only normal but essential in using git.

Point 3

there are merge conflicts in myFile. ... end up with conflict markers in the file.

The default conflict markers injected by git is a very poor solution in helping resolving the conflict, I agree. Luckily there are proper 3-way merge tools you can use instead like for instance KDiff3.

Proper graphical 3-way merge tool one of these tools that after having started using it to resolve merge conflicts you never want to go back to not using one.

Point 4

You probably want to do something like the following:

git checkout -b myfile_updates_from_dev feature
git merge dev

# Undo any automatic resolvement of non-myFile files. The following command automates this,
# if there is just a few files you can alternatively just run `git status` and then
# `git reset file1 file2 file3`.
git status --porcelain=1 | grep ^M | cut -c4- | tr '\012' '\000' | xargs -0 --no-run-if-empty git restore --staged

# Resolve conflicts with a nice graphical 3-way merge tool.
git-resolve-conflict-using-kdiff3
git commit -m "---temporary commit not used directly---"

git checkout feature
# Bringing in the changes from dev but without having it be a merge commit.
git diff feature myfile_updates_from_dev | git apply -
git add myFile
git commit -m "Bring in changes to myFile from dev branch" -m "Corresponding to commit 1234568 (some commit message)."

git branch -D myfile_updates_from_dev

Point 5

The following

git checkout main
git branch deleteme
git checkout deleteme

can be simplified to just

git checkout -b deleteme main
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the detailed comments! P1 Yes, exactly. P2 Ok. P3 But I actually do want the conflict markers. I want the same behavior for myFile that would happen if I did a merge while conflicts are present (markers injected), but I don't want all the other files to also get changed and I don't want everything to be considered merged. P4 This method does the correct thing, but it's basically the same as my new branch method. P5 Cool! I didn't know about the additional <start-point> argument. Anyway, thanks.
Regarding point 3, the conflict marker are not missing here, they are still present and added to the file by git as normal, it is just that the 3-way merge tool ignores those and instead uses the "raw" contributing versions as inputs for its conflict resolution.
0

It's not entirely clear what you're asking for and why, but I think you want to see the merge conflicts for a single file.

Perform the merge, but don't commit it.

git merge --no-commit somebranch

Now you can git diff myFile and look at the conflicts (if any). The rest of the merge will be staged, but not committed.

When you're done you can return to the pre-merge state with git merge --abort if there were conflicts and git restore --staged --worktree if there were not.

5 Comments

Do not both close and answer.
@matt Excuse me, I found the duplicate after my answer. SO likes to close instantly because of my gold Git tag; I wish it gave me a choice.
Thanks for the suggestion, but in this case git merge --no-commit dev is the exact same thing as git merge dev since there are conflicts. So this answer doesn't really add anything.
@nullromo It remains unclear to me what you're asking for, and for what purpose. "I want to combine the changes to myFile from dev and feature and end up with conflict markers in the file." The answer does that, could you explain what is missing? You say "😨 Merge the branches and then reset or revert everything else. I don't even want to think about doing this." Could you explain what is unacceptable to you about that?
I clarified the question. However, please don't spend more of your time on this. The question can remain closed and we can move on. Thanks for the help and comments.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.