





















































This article, by Eric Pidoux, author of Git Best Practices Guide, covers a part of Git that you will definitely meet: conflicts. How can we resolve them?
(For more resources related to this topic, see here.)
While working together as a team on a project, you will work on the same files. The pull command won't work because there are conflicts, and you might have tried some Git commands and things got bad. In this chapter, we will find solutions to these conflicts and see how we can fix them. We will cover the following topics:
Sometimes, you will need to find something inside all your files. You can, of course, find it with the search feature of your OS, but Git already knows all your files.
To search text inside your files, simply use the following command:
Erik@server:~$ git grep "Something to find" Erik@server:~$ git grep -n body Master:Website.Index.html:4: <bodyMaster:Website.Index.html:12: </body>
It will display every match to the given keyword inside your code. All lines use the [commitref]:[filepath]:[linenumber]:[matchingcontent] pattern.
Notice that [commitref] isn't displayed on all Git versions.
You can also specify the commit references that grep will use to search the keyword:
Erik@server:~$ git grep -n body d32lf56 p88e03d HEAD~3 Master:Website.Index.html:4: <body> Master:Website.Index.html:12: </body>
In this case, grep will look into the d32lf56, p88e03d, and third commit starting by the head pointer.
Your repository has to be encoded in UTF-8; otherwise, the grep command won't work.
Git allows you to use regex inside the search feature by replacing somethingToFind with a regex.
You can use the logical operators (or and and), as shown in the following command:
Erik@server:~$ git grep -e myRegex1 --or -e myRegex2 Erik@server:~$ git grep -e myRegex1 --and -e myRegex2
Let's see this with an example. We only have a test.html page inside our last commit, and we want to find whether or not there is a word with an uppercase alphabetic value and numeric values:
Erik@server:~$ git grep -e [A-Z] --and -e [0-9] HEAD Master:Website.Test.html:6: TEST01
With the grep command, you can delve deeper, but it's not necessary to discuss this topic here because you won't use it every day!
The git status command is helpful if you have to analyze your repository:
Erik@server:~$ git status # On branch master # Your branch is ahead of 'origin/master' by 2 commits # (use "git push" to publish your local commits) # Changes not staged for commit: # (use "git add<file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: myFile1 # modified: myFile2 # # Untracked files: # (use "git add<file>..." to include in what will be committed) # # newFile.txt no changes added to commit (use "git add" and/or "git commit -a")
Git analyzes the local repository in comparison to the remote repository. In this case, you have to add newFile.txt, commit myFile1 and myFile2, and push them to the remote repository.
The best way to explore the past commits inside your repository is to use the git log command. For this part, we will assume that there are only two commits.
To display all commits, use the following commands:
Erik@server:~$ git log --all Commit xxxxxxxxxxx Author: Jim <[email protected]> Date: Sun Jul 20 15:10:12 2014 -0300 Fix front bugs on banner Commit xxxxxxxxxxx Author: Erik <[email protected]> Date: Sat Jul 19 07:06:14 2014 -0300 Add the crop feature on website backend
This is probably not what you want. After several days of work, you will have plenty of these commits, so how will you filter it?
The power of the git log command is that you can quickly find anything in all commits.
Let's go for a quick overview of what Git is able to find. We will start by finding the last commit:
Erik@server:~$ git log -1 Commit xxxxxxxxxxx Author: Jim <[email protected]> Date: Sun Jul 20 15:10:12 2014 -0300 Fix front bugs on banner
The number after the git log command indicates that it is the first commit from Head.
Too easy! Let's try to find what the last commit of Erik is:
Erik@server:~$ git log --author=Erik -1 Commit xxxxxxxxxxx Author: Erik <[email protected]> Date: Sat Jul 19 07:06:14 2014 -0300 Add the crop feature on website backend
Now, let's find it between two dates:
Erik@server:~$ git log --author=Erik --before "2014-07-20" --after "2014-07-18" Commit xxxxxxxxxxx Author: Erik <[email protected]> Date: Sat Jul 19 07:06:14 2014 -0300 Add the crop feature on website backend
As I told you earlier, there are a lot of parameters to the git log command. You can see all of them using the git help log command.
The stat parameter is really useful:
Erik@server:~$ git log --author=Jim --stat Commit xxxxxxxxxxx Author: Jim <[email protected]> Date: Sun Jul 20 15:10:12 2014 -0300 Fix front bugs on banner index.php | 1 + 1 file changed, 1 insertion(+)
This parameter allows you to view a summary of the changes made in each commit. If you want to see the full changes, try the -p parameter.
Remember that the git log command has a file parameter to restrict the search to the git log [file] file.
There are two ways to see changes in a repository: git diff and git show.
The git diff command lets you see the changes that are not committed. For example, we have an index.phpfile and replace the file content by a line. Just before the lines, you will see a plus (+) or minus (-) sign. The + sign means that content was added and the – sign denotes that it was removed:
Erik@server:~$ git diff diff --git a/index.php b/index.php indexb4d22ea..748ebb2 100644 --- a/index.php +++ b/index.php @@ -1,11 +1 @@ -<html> - -<head> -<title>Git is great!</title> -</head> -<body> -<?php - echo 'Git is great'; -?> -</body> -</html> +<b> I added a line</b>
If you want to analyze a commit, I suggest you to use the git show command. It will display the full list of changes of the commit:
Erik@server:~$ git show commitId
There is a way to do the opposite, that is, to display commits for a file with git blame:
Erik@server:~$ git blameindex.php e4bac680 (Erik 2014-07-20 19:00:47 +0200 1) <b> I added a line</b>
The first thing to know is that you can always clean your mistake with Git. Sometimes this will be hard or painful for your code, but you can do it!
Let's start this section with how to remove untracked files:
Erik@server:~$ git clean -n
The –n option will make a dry-run (it's always important to see what will happen before you regret it).
If you want to also remove directories and hidden files, use this one:
Erik@server:~$ git clean -fdx
With these options, you will delete new directories (-d) and hidden files (-x) and be able to force them (-f).
The git reset command will allow you to go back to a previous state (for example, commit). The git reset command has three options (soft, hard, or mixed, by default).
In general, the git reset command's aim is to take the current branch, reset it to point somewhere else, and possibly bring the index and work tree along. More concretely, if the master branch (currently checked out) looks like the first row (in the following figure) and you want it to point to B and not C, you will use this command:
Erik@server:~$ git reset B
The following diagram shows exactly what happened with the previous command. The HEAD pointer was reset from C to B:
The following table explains what the options really move:
Option |
Head pointer |
Working tree |
Staging area |
Soft |
Yes |
No |
No |
Mixed |
Yes |
No |
Yes |
Hard |
Yes |
Yes |
Yes |
The three options that you can provide on the reset command can be easily explained:
The git reset command doesn't remove untracked files; use git clean instead.
The git revert command allows you to "cancel" your last unpushed commit. I used quotes around cancel because Git doesn't drop the commit; it creates a new commit that executes the opposite of your commit. A pushed commit is irreversible, so you cannot change it.
Firstly, let's have a look at the last commits:
Erik@server:~$ git log commite4bac680c5818c70ced1205cfc46545d48ae687e Author: Eric Pidoux Date: Sun Jul 20 19:00:47 2014 +0200 replace all commit0335a5f13b937e8367eff35d78c259cf2c4d10f7 Author: Eric Pidoux Date: Sun Jul 20 18:23:06 2014 +0200 commitindex.php
We want to cancel the 0335… commit:
Erik@server:~$ git revert 0335a5f13
Canceling this commit isn't necessary to enter the full commit ID, but just the first characters. Git will find it, but you will have to enter at least six characters to be sure that there isn't another commit that starts with the same characters.
When you are working with several branches, a conflict will probably occur while merging them. It appears if two commits from different branches modify the same content and Git isn't able to merge them.
If it occurs, Git will mark the conflict and you have to resolve it.
For example, Jim modified the index.html file on a feature branch and Erik has to edit it on another branch. When Erik merges the two branches, the conflict occurs.
Git will tell you to edit the file to resolve the conflict. In this file, you will find the following:
<<<<<<< HEAD Changes from Erik ======= Changes from Jim >>>>>>> b2919weg63bfd125627gre1911c8b08127c85f8
The <<<<<<< characters indicate the start of the merge conflict, the ====== characters indicate the break points used for comparison, and >>>>>>> indicate the end of the conflict.
To resolve a conflict, you have to analyze the differences between the two changes and merge them manually. Don't forget to delete the signs added by Git. After resolving it, simply commit the changes.
If your merge conflict is too complicated to resolve because you can't easily find the differences, Git provides a useful tool to help you.
Git's diff helps you to find differences:
Diff --git erik/mergetestjim/mergetest Index.html 88h3d45..92f62w 130634 --- erik/mergetest +++ jim/mergetest @@ -1,3 +1,4 @@ <body> +I added this code between This is the file content -I added a third line of code +And this is the last one
So, what happened? The command displays some lines with the changes, with the + mark coming from origin/master; those marked with – are from your local repository, and of course, the lines without a mark are common to both repositories.
In this article, we covered all tips and commands that are useful to fix mistakes, resolve conflicts, search inside the commit history, and so on.
Further resources on this subject: