如何确定Git是将文件处理为二进制文件还是文本文件?


78

我知道Git会以某种方式自动检测文件是二进制文件还是文本文件,并且.gitattributes可以根据需要手动设置该文件。但是还有一种方法可以询问Git如何处理文件吗?

假设我有一个包含两个文件的Git存储库:一个ascii.dat包含纯文本的binary.dat文件和一个包含随机二进制内容的文件。Git将第一个.dat文件作为文本处理,将第二个文件作为二进制处理。现在,我想编写一个Git Web前端,它具有用于文本文件的查看器和用于二进制文件的特殊查看器(例如,显示十六进制转储)。当然,我可以实现自己的文本/二进制检查,但是如果查看者依赖于Git处理这些文件的信息,它将更加有用。

那么我怎么问Git将文件视为文本文件还是二进制文件呢?

Answers:


40

builtin_diff()1个调用diff_filespec_is_binary(),哪个调用buffer_is_binary()检查在前8000个字节中是否存在零字节(NUL“字符”)(如果较短则为整个长度)。

我看不到这“是二进制的吗?” 测试在任何命令中都显式公开。

git merge-file直接使用buffer_is_binary(),因此您可以使用它:

git merge-file /dev/null /dev/null file-to-test

error: Cannot merge binary files: file-to-test给定二进制文件时,似乎会产生错误消息,并且退出状态为255。我不确定我是否想依靠这种行为。

也许git diff --numstat会更可靠:

isBinary() {
    p=$(printf '%s\t-\t' -)
    t=$(git diff --no-index --numstat /dev/null "$1")
    case "$t" in "$p"*) return 0 ;; esac
    return 1
}
isBinary file-to-test && echo binary || echo not binary

对于二进制文件,--numstat输出应以-TAB -TAB开头,因此我们仅对其进行测试。


1 builtin_diff()具有这样的字符串Binary files %s and %s differ应该很熟悉。


1
在cygwin(Windows)上,/ dev / null不存在。必须使用塞思带来的神奇的SHA1。git diff --numstat 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD -- "$1"
koppor 2012年

30
git grep -I --name-only --untracked -e . -- ascii.dat binary.dat ...

将返回git解释为文本文件的文件名。

您可以使用通配符,例如

git grep -I --name-only --untracked -e . -- *.ps1

适用于更高版本,但对于git 1.7.5.4来说,这仅是致命的:empty(sub)expression。将正则表达式更改为-e .可与此版本一起使用(可能会误识别仅由空行组成的文本文件!)。
2013年

非常喜欢这种测试文件的简单方法。谢谢!
尤金·萨金

我将其更改-e .为更兼容,并添加--untracked到可处理更大范围的文件中。
eckes

19

我不喜欢这个答案,但是您可以解析git-diff-tree的输出以查看它是否为二进制。例如:

git diff-tree -p 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD -- MegaCli 
diff --git a/megaraid/MegaCli b/megaraid/MegaCli
new file mode 100755
index 0000000..7f0e997
Binary files /dev/null and b/megaraid/MegaCli differ

相对于:

git diff-tree -p 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD -- megamgr
diff --git a/megaraid/megamgr b/megaraid/megamgr
new file mode 100755
index 0000000..50fd8a1
--- /dev/null
+++ b/megaraid/megamgr
@@ -0,0 +1,78 @@
+#!/bin/sh
[…]

哦,顺便说一句,4b825d…是一个表示空树的魔术SHA(它一个空树的SHA,但是git特别了解这种魔术)。


2
谢谢你,先生。我用的是git diff-tree --numstat 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD哪种- - filename格式。
John Gietzen

2
如果您想要存储库中所有二进制文件的列表,可以执行git diff --numstat 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD -- | grep "^-" | cut -f 3
bonh 2015年

1
赛斯(Seth)请在最后一句中修正错字,应为4b82 5 d;因此,我不会让我提交1个字符的修改。
chrisinmtown

0

冒着因代码质量差而受到打击的风险,我列出了一个C实用程序is_binary,该实用程序是围绕Git源中的原始buffer_is_binary()例程构建的。请参阅内部注释以了解如何构建和运行。易于修改:

/***********************************************************
 * is_binary.c 
 *
 * Usage: is_binary <pathname>
 *   Returns a 1 if a binary; return a 0 if non-binary
 * 
 * Thanks to Git and Stackoverflow developers for helping with these routines:
 * - the buffer_is_binary() routine from the xdiff-interface.c module 
 *   in git source code.
 * - the read-a-filename-from-stdin route
 * - the read-a-file-into-memory (fill_buffer()) routine
 *
 * To build:
 *    % gcc is_binary.c -o is_binary
 *
 * To build debuggable (to push a few messages to stdout):
 *    % gcc -DDEBUG=1 ./is_binary.c -o is_binary
 *
 * BUGS:
 *  Doesn't work with piped input, like 
 *    % cat foo.tar | is_binary 
 *  Claims that zero input is binary. Actually, 
 *  what should it be?
 *
 * Revision 1.4
 *
 * Tue Sep 12 09:01:33 EDT 2017
***********************************************************/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define MAX_PATH_LENGTH 200
#define FIRST_FEW_BYTES 8000

/* global, unfortunately */
char *source_blob_buffer;

/* From: /programming/14002954/c-programming-how-to-read-the-whole-file-contents-into-a-buffer */

/* From: /programming/1563882/reading-a-file-name-from-piped-command */

/* From: /programming/6119956/how-to-determine-if-git-handles-a-file-as-binary-or-as-text
*/

/* The key routine in this function is from libc: void *memchr(const void *s, int c, size_t n); */
/* Checks for any occurrence of a zero byte (NUL character) in the first 8000 bytes (or the entire length if shorter). */

int buffer_is_binary(const char *ptr, unsigned long size)
{
  if (FIRST_FEW_BYTES < size)
    size = FIRST_FEW_BYTES;
    /* printf("buff = %s.\n", ptr); */
  return !!memchr(ptr, 0, size);
}
int fill_buffer(FILE * file_object_pointer) {
  fseek(file_object_pointer, 0, SEEK_END);
  long fsize = ftell(file_object_pointer);
  fseek(file_object_pointer, 0, SEEK_SET);  //same as rewind(f);
  source_blob_buffer = malloc(fsize + 1);
  fread(source_blob_buffer, fsize, 1, file_object_pointer);
  fclose(file_object_pointer);
  source_blob_buffer[fsize] = 0;
  return (fsize + 1);
}
int main(int argc, char *argv[]) {

  char pathname[MAX_PATH_LENGTH];
  FILE *file_object_pointer;

  if (argc == 1) {
    file_object_pointer = stdin;
  } else {
    strcpy(pathname,argv[1]);
#ifdef DEBUG
    printf("pathname=%s.\n", pathname); 
#endif 
    file_object_pointer = fopen (pathname, "rb");
    if (file_object_pointer == NULL) {
      printf ("I'm sorry, Dave, I can't do that--");
      printf ("open the file '%s', that is.\n", pathname);
      exit(3);
    }
  }
  if (!file_object_pointer) {
    printf("Not a file nor a pipe--sorry.\n");
    exit (4);
  }
  int fsize = fill_buffer(file_object_pointer);
  int result = buffer_is_binary(source_blob_buffer, fsize - 2);

#ifdef DEBUG
  if (result == 1) {
    printf ("%s %d\n", pathname, fsize - 1);
  }
  else {
    printf ("File '%s' is NON-BINARY; size is %d bytes.\n", pathname, fsize - 1); 
  }
#endif
  exit(result);
  /* easy check -- 'echo $?' after running */
}

-10

您可以使用命令行工具“文件”实用程序。在Windows上,它包含在git安装中,通常位于C:\ Program Files \ git \ usr \ bin文件夹中

file --mime-encoding *

在Windows获取文件编码中查看更多信息


11
我不是拒绝投票的人,但毫无疑问,因为``file''与git如何确定文件类型没有任何关系,因此毫无疑问。'file'不使用git代码,并且git不使用'file'命令。例如,“文件”对.gitattributes文件如何帮助git确定文件类型一无所知。
Jim Raden
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.