如何判断Git是将文件作为二进制文件还是文本文件处理?

vsmadaxz  于 2023-01-28  发布在  Git
关注(0)|答案(9)|浏览(183)

我知道Git会自动检测一个文件是二进制文件还是文本文件,如果需要的话,可以使用.gitattributes来手动设置。但是,是否也有办法询问Git如何处理文件呢?
假设我有一个Git仓库,里面有两个文件:一个包含纯文本的ascii.dat文件和一个包含随机二进制内容的binary.dat文件。Git将第一个.dat文件作为文本文件处理,将第二个文件作为二进制文件处理。现在我想编写一个Git web前端,它有一个文本文件查看器和一个二进制文件查看器(例如显示十六进制转储)当然,我可以实现自己的文本/二进制检查,但如果查看器依赖于Git如何处理这些文件的信息,那么它会更有用。
那么我怎样才能问Git它是把一个文件当作文本文件还是二进制文件呢?

tv6aics1

tv6aics11#

builtin_diff()1调用diff_filespec_is_binary()diff_filespec_is_binary()调用buffer_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这样的字符串,应该很熟悉。

cbeh67ev

cbeh67ev2#

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

将返回被git解释为文本文件的文件名。
这里的诀窍在于这两个git grep parameters

  • -I:与二进制文件中的模式不匹配。
  • -e .:正则表达式匹配文件中的任何字符

您可以使用通配符,例如

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

5f0d552i3#

我不喜欢这个答案,但是你可以解析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
[…]

哦,顺便说一句,4 b825 d...是一个魔法SHA,它代表空树(它 * 是 * 空树的SHA,但是git特别注意这个魔法)。

of1yzvn4

of1yzvn44#

# considered binary (or with bare CR) file
git ls-files --eol | grep -E '^(i/-text)'

# files that do not have any line-ending characters (including empty files) - unlikely that this is a true binary file ?
git ls-files --eol | grep -E '^(i/none)'

#                                                        via experimentation
#                                                      ------------------------
#    "-text"        binary (or with bare CR) file     : not    auto-normalized
#    "none"         text file without any EOL         : not    auto-normalized
#    "lf"           text file with LF                 : is     auto-normalized when gitattributes text=auto
#    "crlf"         text file with CRLF               : is     auto-normalized when gitattributes text=auto
#    "mixed"        text file with mixed line endings : is     auto-normalized when gitattributes text=auto
#                   (LF or CRLF, but not bare CR)

来源:https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt---eolhttps://github.com/git/git/commit/a7630bd4274a0dff7cff8b92de3d3f064e321359
哦对了设置.gitattributes文本属性时要小心,例如*.abc text。因为在这种情况下,* 所有 * 包含*.abc的文件都将被规范化,即使它们是二进制文件(在二进制文件中找到的内部CRLF将被规范化为LF)。这与自动行为不同。

wgmfuz8q

wgmfuz8q5#

使用git check-attr --all
无论文件是否已暂存/提交,此操作都有效。
在git版本2.30.2上测试。
假设您在.gitattributes中有这个。

package-lock.json binary

有这样的输出。

git check-attr --all package-lock.json 
package-lock.json: binary: set
package-lock.json: diff: unset
package-lock.json: merge: unset
package-lock.json: text: unset

对于普通文件,没有输出。

git check-attr --all package.json
qjp7pelc

qjp7pelc6#

@bonh在评论中给出了工作答案
git差异--编号统计信息4 b825 dc 642 cb 6 eb 9a 060 e54 bf 8d 69288 fbe 4904标题--|grep“^-”|截止值f3
它显示了所有被git解释为二进制文件的文件。

n3ipq98p

n3ipq98p7#

冒着因为代码质量差而被打耳光的风险,我列出了一个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: https://stackoverflow.com/questions/14002954/c-programming-how-to-read-the-whole-file-contents-into-a-buffer */

/* From: https://stackoverflow.com/questions/1563882/reading-a-file-name-from-piped-command */

/* From: https://stackoverflow.com/questions/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 */
}
wgx48brx

wgx48brx8#

那么我怎样才能问Git它是把一个文件当作文本文件还是二进制文件呢?
不仅git check-attr --all是一个不错的选择,而且在Git 2.40(Q1 2023)中,"git check-attr"(man)学会了使用一个可选的树形结构来读取.gitattributes文件。
这意味着,如果Git将文件视为文本或二进制文件,则可以对任何提交执行Git,而不仅仅是当前的HEAD!

git check-attr --all --source=@~2 -- myFile 

git check-attr --all --source=anotherBranch -- myFile

参见commit 47cfc9bcommit c847e8c(2023年1月14日)和Karthik Nayak ( KarthikNayak )
(由Junio C Hamano -- gitster --合并至commit 577bff3,2023年1月23日)

attr:添加标记--source以使用tree-ish

签署人:卡蒂克·纳亚克
签署人:图恩·克拉斯
合著人:www.example.comtoon@iotcl.com
.gitattributes文件的内容可能会随着时间的推移而变化,但是"git check-attr"(man)总是在工作树和/或索引中检查它们的属性。
有选择地允许用户根据路径检查从提交而不是HEAD中获取的属性可能是有益的。
添加一个新的标记--source,它允许用户根据提交检查属性(实际上任何树型都可以)。
当用户使用这个标志时,我们将遍历.gitattributes文件的堆栈,但不是检查当前工作的树和/或索引,而是检查所提供的树型对象中的blob。
这允许该命令也可以在空存储库中使用。
因为我们使用了一个类似树的对象,所以用户可以传递"--source HEAD:subdirectory",所有的属性都会被查找,就好像subdirectory是仓库的根目录一样。
我们不能简单地使用没有--source标志的<rev>:<path>语法,类似于在git showman)中使用它的方式,因为--之前的任何非标志参数都被视为属性,而--之后的任何参数都被视为路径名。
更改涉及创建新函数read_attr_from_blob,该函数在给定路径的情况下,针对提供的源读取路径的blob,并逐行解析属性。
此函数插入到read_attr()函数中,我们在其中遍历属性文件堆栈。
git check-attr现在在其手册页中包括:
'git check-attr' [--source <tree-ish>] [-a | --all | <attr>...] [--] <pathname>...
x1米20英寸1x
git check-attr现在在其手册页中包括:

--source=<tree-ish>

根据指定的树形结构检查属性。
通常通过命名与之关联的提交、分支或标记来指定源代码树。

w9apscun

w9apscun9#

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

file --mime-encoding *

查看更多Get encoding of a file in Windows

相关问题