Our previous merge and pull commands have gone smoothly because we pushed from local and merged to live. It was a one-way transaction. Things don't always go so smoothly when you work on a team, or for some other reason make changes to the same file in two different locations, then try to merge them. Let's take a look at a simple example of when this might cause an issue and how to fix it.
Modifying a File on the Live Server
In this example, I'm going to modify the "README.txt" file that's inside the "/sites/all/modules" directory.
I'll edit the file on the live server first, as though I were fixing a security issue, and add the line "This is the temporary fix on the live server." (with a blank line before and after it) between lines 3 and 5 so that it becomes the new line 5.
- Switch to the "Remote - Live" terminal tab
- Type "vi sites/all/modules/README.txt"
- Press the "i" key to enter insert mode
- Make the change to the file
- Press the escape key and type ":wq" and press Enter to save and quit
Now that the I've made the change, the appropriate thing to do would be to commit the change, and push it to the central repo so that I (and/or my team) can pull it to our local copies of the repo.
However, let's pretend that this was the last thing we did two minutes before the end of the day on Friday, we're sick, and want to get home to a nice hot bath. Due to our condition we forget to make the commit and push it to the local repo.
This is where conflicts can crop up.
Modifying the Same File on the Local Machine
So we've made our quick security fix on the live machine, but didn't push it to the central repo. Monday morning comes around, and I'm feeling better, so I'm prepared to make the proper, full-fledge security fix.
I'll open up the file "sites/all/modules/README.txt" in my text editor and add the following (with a blank line before and after it) between lines 3 and 5 so that it becomes the new lines 5 and 6.:
This is the full-fledged fix.
This one should be kept.
Then I'll save the file and close it.
Now, when I type "git status" in my "Local" terminal tab, we'll see the file has been modified.
Let's commit this and push it to the central repo.
Pushing the local change to the central repo
- Commit the file by typing "git commit -am "Made the official fix to 'sites/all/modules/README.txt'.""
- Typing "git status" will show that we are 1 commit ahead of origin/master
- Push the change with "git push"
Fixing Merge Conflicts
Now that our "official" fix has been pushed to the central repo, let's pull it to the live server.
- I'll switch to the "Remote - Live" terminal tab
- I'll type "git pull" to pull the changes to the live machine
When I do that, the info from the central repo is fetched, but when it attempts to merge the changes, an error occurs, and Git reports the message:
error: Your local changes to 'sites/all/modules/README.txt' would be overwritten by merge. Aborting.
Please, commit your changes or stash them before you can merge.
This just demonstrates that Git is intelligent enough not to overwrite your changes, even if they haven't been staged yet.
So, let's do what it suggests and commit the changes on the live site.
- I'll type "git commit -am "Performed temporary security fix on live site."" and press Enter
Now, let's try the pull again.
- "git pull"
Okay, this time we get the message:
CONFLICT (content): Merge conflict in sites/all/modules/README.txt
Automatic merge failed; fix conflicts and then commit the result.
So, Git started the auto-merging process but encountered a conflict in the file README.txt. So, it notified us of the conflict and tells us that the auto-merge failed and that we need to fix the conflicts and commit the result.
If we type "git status", we'll see the "Unmerged paths" heading and that the README.txt file has the status of "both modified". This indicates that there is a conflict, so in case you missed the "CONFLICT" message above you'll know about the conflict when you type "git status".
Normally, changes (even to the same file) are merged happily along side one another. In this case however, the change is made to the exact same line, so Git needs to know which one we want to keep.
To help us make the appropriate fix, Git placed both lines in the file, and identified the trouble area by surrounding it with a series of "<" and ">" symbols. Let's open up the file and take a look.
- "vi sites/all/modules/README.txt"
In our example, we can see the error clearly, because our file is short, and there's only one error. If you have a long file, and/or multiple issues, it's a good idea to search for the "<<<<<<<" string to find each issue. In VI, you do that in command mode by typing "/string" where "string" is your search term.
- I'll type "/<<<<<<<" and press Enter (if there were more than one issues, pressing the "n" key moves to the next search result, and "N" moves to the previous result)
In its current state, if this were a module file, or contained functional code, this would probably break functionality, so you need to fix it as quickly as possible.
To fix the file, you need to delete everything starting with the "<<<<<<<" through the end of the hash after the ">>>>>>>", keeping only the final change.
The two versions are separated by "=======" and I'll keep the second one, so I'll press the "d" key twice on each line that I would like to delete. (Typing "dd" in VI deletes the current line when in command mode.)
Once I have the file the way I want it, I'll save and quit VI by typing ":wq" (while in command mode) and pressing Enter.
Pushing the Corrected File to the Central Repo
Now that we've fixed the conflict, let's type "git status" to check the status of our repository. When we do, we'll see that the file still has the status of "both modified". In order to commit the fixed file, we simply need to add the file to the staging area.
- "git add sites/all/modules/README.txt"
Now, "git status" will show that our branch has diverged from 'origin/master', but that the file has been modified and is ready to be committed. So, let's go ahead and commit it.
- "git commit -am "Resolved conflict in sites/all/modules/README.txt""
Now, we can check the git log to verify our changes.
- "git log --oneline"
In the log, we'll see both of our fixes, the "official" fix and the "temporary" fix, and that we resolved the conflict. You'll notice that the "official" fix appears to have been done first. This is because technically, we did make that commit before the temporary fix commit, which is when we ran into the conflict.
Now that we have the fix in place, we need to push it to the central remote repo, and pull it to our local repo to avoid future conflicts.
- "git push"
- Switch to "Local" terminal tab
- "git pull"
Now "git log --oneline" will show all of the commits on our local machine and if we open up the file, we'll see just the "official" fix.