Wie kann man feststellen, ob Git eine Datei als Binärdatei oder als Text behandelt?

78

Ich weiß, dass Git irgendwie automatisch erkennt, ob eine Datei binär oder textuell ist, und dass .gitattributesdies verwendet werden kann, um dies bei Bedarf manuell einzustellen. Aber gibt es auch eine Möglichkeit, Git zu fragen, wie eine Datei behandelt wird?

Nehmen wir also an, ich habe ein Git-Repository mit zwei Dateien: Eine ascii.datDatei mit Klartext und eine binary.datDatei mit zufälligen Binärdateien. Git behandelt die erste .datDatei als Text und die sekundäre Datei als Binärdatei. Jetzt möchte ich ein Git-Web-Frontend schreiben, das einen Viewer für Textdateien und einen speziellen Viewer für Binärdateien enthält (z. B. Anzeige eines Hex-Dumps). Sicher, ich könnte meine eigene Text- / Binärprüfung implementieren, aber es wäre nützlicher, wenn sich der Betrachter auf die Informationen verlassen würde, wie Git mit diesen Dateien umgeht.

Wie kann ich Git fragen, ob eine Datei als Text oder Binärdatei behandelt wird?

Kayahr
quelle

Antworten:

40

builtin_diff()1 Aufrufe, diff_filespec_is_binary()die buffer_is_binary()aufrufen, ob in den ersten 8000 Bytes (oder bei Verkürzung über die gesamte Länge) ein Null-Byte (NUL-Zeichen) vorkommt.

Ich sehe nicht, dass dies "ist es binär?" test wird jedoch in jedem Befehl explizit angezeigt.

git merge-filedirekt verwendet buffer_is_binary(), so dass Sie möglicherweise davon Gebrauch machen können:

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

Es scheint die Fehlermeldung wie zu erzeugen error: Cannot merge binary files: file-to-testund ergibt einen Exit-Status von 255, wenn eine Binärdatei angegeben wird. Ich bin mir nicht sicher, ob ich mich auf dieses Verhalten verlassen möchte.

Vielleicht git diff --numstatwäre zuverlässiger:

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

Bei Binärdateien sollte die --numstatAusgabe mit -TAB -TAB beginnen, daher testen wir dies nur.


1 builtin_diff() hat Binary files %s and %s differsolche Saiten sollte bekannt sein.

Chris Johnsen
quelle
1
Unter cygwin (Windows) existiert / dev / null nicht. Man muss den magischen SHA1 verwenden, den Seth hervorgebracht hat. git diff --numstat 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD -- "$1".
Koppor
30
git grep -I --name-only --untracked -e . -- ascii.dat binary.dat ...

gibt die Namen von Dateien zurück, die git als Textdateien interpretiert.

Sie können Platzhalter verwenden, z

git grep -I --name-only --untracked -e . -- *.ps1
cstork
quelle
Funktioniert mit späteren Versionen, aber mit Git 1.7.5.4 ergibt dies nur einen fatalen: leeren (Unter-) Ausdruck . Ändern des regulären Ausdrucks in -e .diese Version (möglicherweise auf Kosten der falschen Identifizierung von Textdateien, die nur aus Leerzeilen bestehen!).
John Marshall
Diese einfache Art, die Datei zu testen, hat mir sehr gut gefallen. Vielen Dank!
Eugene Sajine
Ich habe es geändert -e ., um kompatibler zu sein, und hinzugefügt --untracked, um mit einer größeren Auswahl an Dateien zu arbeiten.
eckes
19

Diese Antwort gefällt mir nicht, aber Sie können die Ausgabe von git-diff-tree analysieren, um festzustellen, ob sie binär ist. Zum Beispiel:

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

im Gegensatz zu:

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
[…]

Oh, und übrigens, 4b825d… ist eine magische SHA, die den leeren Baum darstellt (es ist die SHA für einen leeren Baum, aber Git ist sich dieser Magie besonders bewusst).

Seth Robertson
quelle
2
Vielen Dank, guter Herr. Ich habe verwendet, git diff-tree --numstat 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEADdie das - - filenameFormat hat.
John Gietzen
2
Wenn Sie eine Liste aller Binärdateien in Ihrem Repo möchten, können Sie dies tungit diff --numstat 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD -- | grep "^-" | cut -f 3
Bonh
1
Seth bitte Tippfehler im letzten Satz korrigieren, sollte 4b82 5 d sein; SO lässt mich keine 1-Zeichen-Bearbeitung einreichen.
Chrisinmtown
0

Auf die Gefahr hin, wegen schlechter Codequalität geschlagen zu werden, liste ich das C-Dienstprogramm is_binary auf, das auf der ursprünglichen Routine buffer_is_binary () in der Git-Quelle basiert. In den internen Kommentaren erfahren Sie, wie Sie erstellen und ausführen. Leicht modifizierbar:

/***********************************************************
 * 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 */
}
yoder2000
quelle
-10

Sie können das Befehlszeilenprogramm 'Datei' verwenden. Unter Windows ist es in der Git-Installation enthalten und befindet sich normalerweise im Ordner C: \ Programme \ git \ usr \ bin

file --mime-encoding *

Weitere Informationen finden Sie unter Codierung einer Datei in Windows abrufen

Michael Freidgeim
quelle
11
Ich bin nicht derjenige, der es herabgestimmt hat, aber es wurde zweifellos herabgestimmt, weil 'file' nichts damit zu tun hat, wie git den Dateityp bestimmt. 'file' verwendet keinen Git-Code und git verwendet nicht den Befehl 'file'. 'file' weiß zum Beispiel nichts darüber, wie die .gitattributes-Datei git dabei hilft, den Dateityp zu bestimmen.
Jim Raden