30

Is there any way of preventing files with merge conflict from getting committed in git? No one is going to commit files with conflict intentionally. But is there a way to prevent files from getting committed in git?

Does git have any settings or configurable values anywhere, where it can prevent files by looking for <<<<<<<, =======, >>>>>>>, or ||||||| ¹ symbols?

7
  • 2
    add git rev-parse -q --verify HEAD && sed -n '/^<<<<<<< /Q1' $(git diff --diff-filter=M --name-only HEAD $(git write-tree)) || { echo "It looks like rain."; exit 1; } to .git/hooks/pre-commit. That's keyboard-to-editbox but it should be substantially correct. Commented Jun 13, 2014 at 21:52
  • Can you tell me what the above condition does, since it does not work for me. I added the condition in pre-commit file and tried adding a new file in git bash, the file got added, then committed it which became successful as well. Let me know if i have missed something. Commented Jun 16, 2014 at 6:38
  • Does the git plugins provide this restriction? Commented Jun 16, 2014 at 6:56
  • @jthill pre-commit works as expected, but i used the same condition in pre-receive which fails. I commit the file using egit in eclipse but i have pre-receive in server, but on pushing the conflict file, the pre-receive hook gets called but still goes ahead with the push. I expected the file not to get pushed. Commented Jun 20, 2014 at 1:00
  • 1
    @jthill: Would you post it as an answer anyway? Lots of people happening on this page will be using straight git, not Eclipse. VonC's answer doesn't work for me, unless I track down some Perl dependency. I also don't need the additional console.log and other checks. Commented Dec 20, 2014 at 0:01

5 Answers 5

14

VonC's answer already explains the different kinds of hooks where you might want to check for merge commits.

If you just want a simple solution to avoid committing conflicts, that is already included with git in the default pre-commit hook sample. Just enable the hook by renaming .git/hooks/pre-commit.sample source to .git/hooks/pre-commit. If you then try to commit a conflict:

$ git commit -am "Fix crash in frobnicate" src/foo.c:239: leftover conflict marker 

Note:

The script uses git diff-index --check internally line 49, which also checks for various white space problems - so you might get whitespace errors as well. You can also run git diff-index --check manually before committing to find problems. See the manpage for details and config options.

This was introduced back in 2008, so your version of git likely has it :-).

Sign up to request clarification or add additional context in comments.

6 Comments

The pre-commit hook sample I get is about non-ASCII characters in the committed filenames. Can you give the content of the no-conflict sample?
@Janis: That's the right one. The script invokes git diff-index --check, which will check for conflict markers. Just try it :-).
I know, quite some time passed since you did react to this, but... in my pre-commit.sample there is no git diff-index --check; nor does it detect leftover merge makers when activated anyway :(
@ConfusedMerlin: Well, the default sample pre-commit hook still contains git diff-index --check: github.com/git/git/blob/master/templates/… . Try creating a fresh repo ( git init ). If you still don't get the pre-commit sample hook, ask a new question :-).
ah, found it just one line outside my terminal sigh; anyway, i can happily commit merge-markers even with that hook enables :( So, yea, a new question it should be.
|
7

You can use a pre-commit hook, but be aware that a git commit --no-verify would effectively ignore that.

I generally put a pre-receive hook in order to control in a (more) central point what is being pushed.

But a pre-commmit allows for a more timely detection (earlier in the development cycle).

Here is another example (in addition of jthill's comment), in perl.
It uses git diff-index -p -M --cached HEAD, that is git diff-index instead of git diff.
I have left a few other controls done by this hook, just to showcase the kind of checks you can do in such a script.

#!/usr/bin/perl use Modern::Perl; use File::Basename; my $nb_errors = 0; my $filepath; for my $l ( split '\n', `git diff-index -p -M --cached HEAD` ) { if ( $l =~ /^diff --git a\/([^ ]*) .*$/ ) { $filepath = $1; } given ( $l ) { # if there is a file called *.log, stop when ( /\.log/ ) { say "$filepath contains console.log ($l)"; $nb_errors++; } # check if there is a warn, that could be unconditionnal, but just warn, don't stop when ( /^[^(\#|\-)]+warn/ ) { # stay silent if we're in a template, a "warn" here is fair, it's usually a message or a css class unless ($filepath =~ /\.tt$/) { say "$filepath contains warn ($l)"; } } # check if there is a ` introduced, that is a mysqlism in databases. Can be valid so don't stop, just warn when (/^\+.*`.*/) { say "$filepath contains a ` ($l)"; } # check if there is a merge conflict marker and just warn (we probably could stop if there is one) when ( m/^<<<<<</ or m/^>>>>>>/ or m/^======/ ) { say "$filepath contains $& ($l)"; } } } if ( $nb_errors ) { say "\nAre you sure you want to commit ?"; say "You can commit with the --no-verify argument"; exit 1; } exit 0; 

10 Comments

Using git plugin for eclipse i was able to commit the file and push as well, with pre-commit and pre-push files in hooks folder. But file doesn't get committed or pushed using git bash. Doesn't git plugin provide the restriction feature?
@user3586664 the file would be committed in git bash if you use git add and git commit.
may be i was not clear, i had commands to stop files from getting committed when it encounters "<<<<" pattern in the file. The problem is, using git bash the file is not getting committed, which is as expected. But on using git plugin for eclipse the restriction fails, which means the file gets committed and pushed as well. Is there anyway to get this restriction applied on git plugin?
@user3586664 it is possible the EGit/JGit doesn't fully support git hooks yet: stackoverflow.com/a/6236026/6309
|
3

A straightforward approach using a pre-commit hook, adapted from here but improved to be a bit more careful and thorough:

#!/bin/sh changed=$(git diff --cached --name-only) if [[ -z "$changed" ]]; then exit 0 fi echo $changed | xargs egrep '^[><=]{7}( |$)' -H -I --line-number # If the egrep command has any hits - echo a warning and exit with non-zero status. if [ $? == 0 ]; then echo "WARNING: You have merge markers in the above files. Fix them before committing." echo " If these markers are intentional, you can force the commit with the --no-verify argument." exit 1 fi 

Don't forget to make the hook executable (chmod u+x pre-commit)!

I've since put this on github: https://github.com/patrickvacek/git-reject-binaries-and-large-files/blob/master/pre-commit

Comments

1

A client side solution is using git pre-commit hooks which allow you to execute specific commands when you commit (a merge is a type of commit) and if they fail, then you cannot merge until it's resolved.

pre-commit is a simple way and standardised to manage git pre-commit hooks.

In the root of your repository create a file called .pre-commit-config.yaml With the following

# See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.0.1 hooks: - id: check-merge-conflict 
Install pre-commit

(preferred way is to) use pipx or do this outside a virtual env since it's a CLI application

python3 -m pip install pre-commit 
Install the hooks
pre-commit install # you only do this once per "git clone" 

Next time you merge if there are conflict markers detected it will warn you and prevent you from continuing

$ git merge --continue [INFO] Checking merge-conflict files only. Check for merge conflicts................................................Failed - hook id: check-merge-conflict - exit code: 1 Merge conflict string "<<<<<<< " found in README.rst:1 Merge conflict string "======= " found in README.rst:3 Merge conflict string ">>>>>>> " found in README.rst:5 

Comments

0

I added a unit test to go through all files in the solution directory for the conflict marker string

[TestClass] public class SolutionValidationTests { [TestMethod] public void CheckForMergeConflicts() { var solutionValidationScripts = new SolutionValidationScripts(); var mergeConflictCheckOkay = solutionValidationScripts.CheckForGitMergeConflict(); Assert.IsTrue(mergeConflictCheckOkay); } } 

SolutionValidationScripts defined separately below:

public class SolutionValidationScripts { public bool CheckForGitMergeConflict() { var failCount = 0; System.Diagnostics.Debug.WriteLine($"{DateTime.Now.ToString(@"dd-MMM-yyyy HH:mm:ss")}: Starting"); var currentDirectory = System.IO.Directory.GetCurrentDirectory(); var sourceFolder = ""; // Change to suit the build location of your solution sourceFolder = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(System.IO.Directory.GetCurrentDirectory()))); // break up the string so this file doesn't get flagged in the test string searchWord = "<<<<<<< " + "HEAD"; List<string> allFiles = new List<string>(); AddFileNamesToList(sourceFolder, allFiles); for (int i = 0; i < allFiles.Count; i++) { // 35 sec var fileName = allFiles[i]; string contents = File.ReadAllText(fileName); if (contents.Contains(searchWord)) { // For faster result.. no need to continue once a problem is found // throwing an exception here actually works better than an early return to help resolve the issue throw new Exception(fileName); } } return (failCount == 0); } private void AddFileNamesToList(string sourceDir, List<string> allFiles) { string[] fileEntries = Directory.GetFiles(sourceDir); foreach (string fileName in fileEntries) { allFiles.Add(fileName); } //Recursion string[] subdirectoryEntries = Directory.GetDirectories(sourceDir); foreach (string item in subdirectoryEntries) { // Avoid "reparse points" if ((File.GetAttributes(item) & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint) { AddFileNamesToList(item, allFiles); } } } } 

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.