Git SHA1 की फ़ाइल बिना Git के कैसे असाइन करें?


138

जैसा कि मैं इसे समझता हूँ जब Git किसी SHA1 हैश को एक फ़ाइल में असाइन करता है तो यह SHA1 अपनी सामग्री के आधार पर फ़ाइल के लिए अद्वितीय होता है।

परिणामस्वरूप यदि कोई फ़ाइल एक रिपॉजिटरी से दूसरे SHA1 में फ़ाइल के लिए चलती है तो वही रहती है क्योंकि उसकी सामग्री नहीं बदली गई है।

Git SHA1 को पचाने की गणना कैसे करता है? यह पूर्ण असम्पीडित फ़ाइल सामग्री पर करता है?

मैं SHA1 को Git के बाहर असाइन करने का अनुकरण करना चाहूंगा।




जवाबों:


255

यह है कि Git किसी फ़ाइल के लिए SHA1 की गणना कैसे करता है (या, Git शब्दों में, "बूँद"):

sha1("blob " + filesize + "\0" + data)

इसलिए आप इसे आसानी से Git इंस्टॉल किए बिना खुद से गणना कर सकते हैं। ध्यान दें कि "\ 0" NULL-बाइट है, न कि दो-वर्ण स्ट्रिंग।

उदाहरण के लिए, एक खाली फ़ाइल का हैश:

sha1("blob 0\0") = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"

$ touch empty
$ git hash-object empty
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391

एक और उदाहरण:

sha1("blob 7\0foobar\n") = "323fae03f4606ea9991df8befbb2fca795e648fa"

$ echo "foobar" > foo.txt
$ git hash-object foo.txt 
323fae03f4606ea9991df8befbb2fca795e648fa

यहाँ एक पायथन कार्यान्वयन है:

from hashlib import sha1
def githash(data):
    s = sha1()
    s.update("blob %u\0" % len(data))
    s.update(data)
    return s.hexdigest()

क्या यह उत्तर पायथन 2 मान रहा है? जब मैं पायथन 3 पर यह कोशिश करता हूं तो मुझे TypeError: Unicode-objects must be encoded before hashingपहली s.update()पंक्ति पर एक अपवाद मिलता है ।
मार्क बूथ

3
अजगर 3 के साथ आपको डेटा को एनकोड करने की आवश्यकता है: s.update(("blob %u\0" % filesize).encode('utf-8'))से बचने के लिए TypeError
मार्क बूथ

Utf-8 के रूप में एन्कोडिंग काम करेगा, लेकिन शायद पहले से ही इसे बाइट स्ट्रिंग से बनाना बेहतर होगा (utf-8 एन्कोडिंग काम करता है क्योंकि कोई भी यूनिकोड वर्ण गैर-ASCII नहीं है)।
torek

उल्लेख के लायक एक अतिरिक्त बात यह है कि git हैश-ऑब्जेक्ट "\ n" को डेटा की "\ n" सामग्री के साथ बदलने के लिए भी लगता है। यह बहुत अच्छी तरह से "\ r" की पूरी तरह से पट्टी कर सकता है, मैंने इसकी जांच नहीं की।
user420667

1
मैंने एक फ़ाइल और ट्री हैश जेनरेटर के पायथन 2 + 3 (दोनों में) लागू किया है: यहाँ github.com/chris3torek/scripts/blob/master/githash.py (ट्री हैशर एक डायरेक्टरी ट्री जोड़ता है)।
torek

17

थोड़ा गुडी: खोल में

echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sum

1
मैं echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sumके उत्पादन की तुलना कर रहा हूं git hash-object path-to-fileऔर वे अलग-अलग परिणाम देते हैं। हालाँकि, echo -e ...सही परिणाम उत्पन्न करता है, सिवाय एक अनुगामी - ( कोई अनुगामी वर्ण git hash-objectउत्पन्न करता है ) को छोड़कर । क्या यह ऐसी चीज है जिसकी मुझे चिंता करनी चाहिए?
FrustratedWithFormsDesigner 21

2
@FrustratedWithFormsDesigner: अनुगामी -का उपयोग sha1sumअगर स्टैन से हैश की गणना करके किया जाता है, तो फ़ाइल से नहीं। किसी बारे में चिन्ता की जरूरत नहीं। अजीब बात है -n, के बारे में है , कि आम तौर पर गूंज द्वारा संलग्न newline को दबाने चाहिए। क्या किसी भी संयोग से आपकी फ़ाइल में एक अंतिम अंतिम पंक्ति है, जिसे आप अपने CONTENTSचर में जोड़ना भूल गए हैं ?
knittl

हां, आप सही हैं। और मुझे लगा कि sha1sum का आउटपुट केवल हैश होना चाहिए , लेकिन इसे सेड या कुछ के साथ निकालना मुश्किल नहीं है।
FrustratedWithFormsDesigner

@FrustratedWithFormsDesigner: यदि आप का उपयोग आप एक ही उत्पादन मिलेगा cat file | sha1sumबजाय sha1sum file(अधिक प्रक्रियाओं और पाइपिंग हालांकि)
Knittl

8

यदि आप गिट स्थापित नहीं है, तो आप इसे आसानी से गणना करने के लिए एक बैश शेल फ़ंक्शन कर सकते हैं।

git_id () { printf 'blob %s\0' "$(ls -l "$1" | awk '{print $5;}')" | cat - "$1" | sha1sum | awk '{print $1}'; }

1
थोड़ा छोटा (stat --printf="blob %s\0" "$1"; cat "$1") | sha1sum -b | cut -d" " -f1:।
sschuberth

4

Git-hash-object के लिए मैन पेज देखें । आप इसका उपयोग किसी विशेष फ़ाइल के git हैश की गणना करने के लिए कर सकते हैं। मुझे लगता है कि git हैश एल्गोरिथ्म में फ़ाइल की सामग्री से अधिक फ़ीड करता है, लेकिन मुझे यकीन नहीं है, और अगर यह अतिरिक्त डेटा में फ़ीड करता है, तो मुझे नहीं पता कि यह क्या है।


2
/// Calculates the SHA1 for a given string
let calcSHA1 (text:string) =
    text 
      |> System.Text.Encoding.ASCII.GetBytes
      |> (new System.Security.Cryptography.SHA1CryptoServiceProvider()).ComputeHash
      |> Array.fold (fun acc e -> 
           let t = System.Convert.ToString(e, 16)
           if t.Length = 1 then acc + "0" + t else acc + t) 
           ""
/// Calculates the SHA1 like git
let calcGitSHA1 (text:string) =
    let s = text.Replace("\r\n","\n")
    sprintf "blob %d%c%s" (s.Length) (char 0) s
      |> calcSHA1

यह F # में एक समाधान है।


मुझे अभी भी umlauts के साथ समस्या है: calcGitSHA1 ("ü")। shouldBeEqualTo। किसी भी विचार कैसे हैश-वस्तु umlauts संभालती है?
forki23

इसे बूँद को बाइटस्ट्रीम के रूप में संभालना चाहिए, इसका मतलब है कि ü की लंबाई 2 (यूनिकोड) है, F will की लंबाई संपत्ति 1 लंबाई होगी (क्योंकि यह केवल एक दृश्य चरित्र है)
knittl

लेकिन System.Text.Encoding.ASCII.GetBytes ("ü") 1 तत्व के साथ बाइट सरणी देता है।
forki23 11

UTF8 और 2 को स्ट्रिंग लंबाई के रूप में उपयोग करने से एक बाइट सरणी मिलती है: [98; 108; 111; 98; 32; 50; 0; 195; 188] और उसके बाद 99fe40df261f7d4afd1391fe2739b2c7466fe968 का SHA1 है। जो git SHA1 भी नहीं है।
forki23

1
चरित्र के तार के लिए आपको कभी भी डिग्गी नहीं लगानी चाहिए। इसके बजाय आपको उन्हें बाइट स्ट्रिंग्स (बाइट सरणियों) पर लागू करना होगा जो आप एक स्पष्ट एन्कोडिंग का उपयोग करके एक चरित्र स्ट्रिंग को बाइट्स में परिवर्तित करके प्राप्त कर सकते हैं।
डोलमेन

2

पूर्ण पायथन 3 कार्यान्वयन:

import os
from hashlib import sha1

def hashfile(filepath):
    filesize_bytes = os.path.getsize(filepath)

    s = sha1()
    s.update(b"blob %u\0" % filesize_bytes)

    with open(filepath, 'rb') as f:
        s.update(f.read())

    return s.hexdigest() 

2
क्या आप वास्तव में चाहते हैं ASCII एन्कोडिंग है। UTF8 केवल यहां काम करता है क्योंकि यह ASCII के साथ संगत है और "बूँद x \ 0" में केवल कोड के साथ वर्ण शामिल हैं <= 127.
फर्डिनेंड Beyer

1

पर्ल में:

#!/usr/bin/env perl
use Digest::SHA1;

my $content = do { local $/ = undef; <> };
print Digest::SHA1->new->add('blob '.length($content)."\0".$content)->hexdigest(), "\n";

एक शेल कमांड के रूप में:

perl -MDigest::SHA1 -E '$/=undef;$_=<>;say Digest::SHA1->new->add("blob ".length()."\0".$_)->hexdigest' < file


1

रूबी का उपयोग करना, आप कुछ इस तरह कर सकते हैं:

require 'digest/sha1'

def git_hash(file)
  data = File.read(file)
  size = data.bytesize.to_s
  Digest::SHA1.hexdigest('blob ' + size + "\0" + data)
end

1

थोड़ा बैश स्क्रिप्ट जो समान आउटपुट का उत्पादन करे git hash-object:

#!/bin/sh
( 
    echo -en 'blob '"$(stat -c%s "$1")"'\0';
    cat "$1" 
) | sha1sum | cut -d\  -f 1

0

जावास्क्रिप्ट में

const crypto = require('crypto')
const bytes = require('utf8-bytes')

function sha1(data) {
    const shasum = crypto.createHash('sha1')
    shasum.update(data)
    return shasum.digest('hex')
}

function shaGit(data) {
    const total_bytes = bytes(data).length
    return sha1(`blob ${total_bytes}\0${data}`)
}

-4

यह ध्यान रखना दिलचस्प है कि जाहिर तौर पर Git डेटा के अंत में एक नयालाइन वर्ण जोड़ता है इससे पहले कि यह हैशेड होगा। एक फ़ाइल जिसमें "हैलो वर्ल्ड!" 980a0d5 का एक बूँद हैश प्राप्त होता है ..., जो इस प्रकार है:

$ php -r 'echo sha1("blob 13" . chr(0) . "Hello World!\n") , PHP_EOL;'

4
उस नई पंक्ति को आपके पाठ संपादक द्वारा जोड़ा जा रहा है, द्वारा नहीं git hash-object। ध्यान रहे कि ऐसा echo "Hello World!" | git hash-object --stdinदेता है 980a0d5..., का उपयोग करते समय echo -nका एक हैश देता है c57eff5...बजाय।
bdesham
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.