prevent file with merge conflicts from getting committed in git

流过昼夜 提交于 2020-05-24 21:21:07

问题


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?


回答1:


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 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 --check internally, which also checks for various white space problems - so you might get whitespace errors as well. You can also run git diff --check before committing to find problems. See the manpage of git diff for details and config options.

This is valid for git V2.0; no idea when it was introduced.




回答2:


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;



回答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




回答4:


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);
            }
        }
    }
}


来源:https://stackoverflow.com/questions/24213948/prevent-file-with-merge-conflicts-from-getting-committed-in-git

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!