问题
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