So I\'m working in a branch, make some changes, and run git merge master
. I get a merge conflict on one of the files I modified (which I know how to deal with),
The reason this happened is because you most likely did the opposite of what you were intending to do.
Let's suppose your working branch is called topic-branch
.
Instead of doing:
$ git merge master
you could have done:
$ git checkout master
$ git merge topic-branch
In English, instead of merging the master
branch into topic-branch
you could have merged topic-branch
into master
.
To understand why this achieves the desired result we can examine the statement made in a previous answer:
When you attempt a merge, all files that can be automatically merged (e.g. where you don't have any changes in your local branch but which have been modified on the source branch), are automatically merged and staged.
The problem you are having is simply merge
trying to do its job. The files aren't changed in your topic branch, but they are on the branch you are merging into it. If you look at this in the opposite direction of merging the topic-branch
into master
the problem goes away because it only considers the files you've modified.
Conceptually, here is what merge is doing (more here):
Let the current head be called current, and the head to be merged called merge.
- Identify the common ancestor of current and merge. Call it ancestor-commit.
- Deal with the easy cases. If the ancestor-commit equals merge, then do nothing. If ancestor-commit equals current, then do a fast forward merge.
- Otherwise, determine the changes between the ancestor-commit and merge.
- Attempt to merge those changes into the files in current.
- If there were no conflicts, create a new commit, with two parents, current and merge. Set current (and HEAD) to point to this new commit, and update the working files for the project accordingly.
- If there was a conflict, insert appropriate conflict markers and inform the user. No commit is created.
I encountered the same problem myself, and came up with an intermediate solution. Need to find a better one down the road.
First to address the question posed by @NoufalIbrahim: As for " I don't want any of these not-by-me changes to get committed.", why did you do a merge at all if you don't want any changes?
You have misunderstood @grautur intention. The changes are needed, but not as part of a new commit. For example, 1 file was added locally, 100 files came from merge. The new commit should have 1 changed file and not 101 changed file. This is especially important if an automatic merge is not possible, but a pull request is initiated and someone has to review the commit. You want the reviewer to review 1 file, not 101 files.
What I am currently doing is this: let's say we have branch 'master' and 'feature'. 'feature' is created from 'master' and I only make changes to files in 'feature'. When new changes are pulled into 'master', git merge master
inside 'feature' will introduce new files (and these are staged automatically in VSCode, which is the IDE I use).
What I do next is unstage all of these files. Basically ignore them. Only add and commit files that I change. Push 'feature' to origin/remote repo, create pull request. When request is accepted and commit is merged to the main branch, delete 'feature' locally and remotely. Pull changes to 'master' local and create a new branch to work on new feature. This new branch will not have a bunch of unstaged files.
There could a git command to tell the git to ignore a bunch of files without using .gitignore. Doing further research into this.
When you attempt a merge, all files that can be automatically merged (e.g. where you don't have any changes in your local branch but which have been modified on the source branch), are automatically merged and staged. Files which could not be automatically merged are updated in your working area with conflict markers and you have to fix them.
Git always assembles new commits in the staging area before committing them. The merge does the same thing. A new commit with all the changes from the source branch is created in the staging area. In case of a conflict, this process of updating the staging area is interrupted and control is given to you. That's why this happens. Once you commit, a "merge commit" will get created in the repository that has both the source and target branches as parents.
As for " I don't want any of these not-by-me changes to get committed.", why did you do a merge at all if you don't want any changes?
is using a word file to cause the merge failure. Here's how to reproduce the issue:
create a repo a
:
mkdir a
cd a
git init
create a/alpha.docx
and save it, then continue with
git add alpha.docx
git commit -m "initial alpha commit"
Create a repo b
that has a
as remote:
cd ..
mkdir b
cd b
git clone ../a
Modify a/alpha.docx
and save it, then continue with
cd ../a
touch beta # create some file
git commit -am "modified alpha"
Open b/alpha.docx
in word and start typing something. Do not save it. This marks the file as busy.
cd ../b
git pull
This will create the file b/beta
, and then abort the merge with this error:
remote: Counting objects: 3, done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From /cygdrive/c/tmp2/b/../a a797c43..5c6796e master -> origin/master Updating a797c43..5c6796e error: unable to unlink old 'alpha.docx': Device or resource busy
If you now try to pull again, after closing word and discarding your local changes, this happens:
$ git pull
Updating a797c43..5c6796e
error: The following untracked working tree files would be overwritten by merge:
beta
Please move or remove them before you merge.
Aborting
git reset --hard HEAD
git clean -f -d
git pull
as suggested here
git add -A
git stash
git pull
git stash drop # optional
I don't know. Feel encouraged to edit this section if you know it.
I, personally, would have expected git to remove all the new files when aborting the merge.
To quote Noufal Ibrahim's answer above:
A new commit with all the changes from the source branch is created in the staging area. In case of a conflict, this process of updating the staging area is interrupted and control is given to you. That's why this happens.
I think the problem with this GIT way of doing things is that after the commit, a "push" will be performed. The "push" will include all committed files - including files that the "pusher" didn't touch. This makes tracking changed very complicated.
Where I find this pop up is if I:
1) create, and work on a branch (b1)
---m
\
b1
---m
\
---b1
2) create a new branch (b2) from b1, during which time someone else makes changes to completely unrelated code in master
----------------m
\
---b1
\
b2
3) go back and make some changes to b1, and then pull b1 into master.
----------------m
\ /
-------b1
\
--b2
If I then try to pull from master into b2, I'll get a conflict because of the changes made to b1 after creating b2. If I fix the conflict, the resulting commit will include all changes that have occurred on master since the branch of b1, as if I had performed them. (git blame
seems to be able to tell the original author though)
In this scenario, if I instead first pull from b1 into b2, and sort out the merge there, then I can then pull from master without issue, and the pulled-in irrelevant commits won't show up as changes I've made.
git merge --abort
is your friend, if you're in the middle of a merge full of commits you didn't make.