簡體   English   中英

GitHub Rest Api 包含特殊字符的文件的 SHA

[英]GitHub Rest Api SHA of file that contains special characters

主要問題

此代碼有效,但areReallyEqual不是必需的。 事實上,不包含特殊字符(“ñ”、“é”、“à”等)的文件沒有必要,所以:我的所有文件除了其中的 2 個。

if (internalFileTextSha !== repositoryFile.sha) {
  // The sha might not be equal due to special characters in the text ("ñ", "é", "à", etc)... doing a second check...
  const remoteFileText = this._gitHubApi.fetchGitHubGetUrl(repositoryFile.download_url).getContentText(); 
  const remoteFileTextSha = GitHubCrypto.getSha(remoteFileText);
  const areReallyEqual = remoteFileTextSha === internalFileTextSha;
  if(!areReallyEqual) {
    // The file has been modified, creating a new commit with the updated file...
    this._gitHubApi.commitUpdatedFile(repositoryName, repositoryFile, internalFile.source);
  } else {
    // The second check determined that both files were equal
  }         
}

更多細節

internalFile來自這里:

  /**
  * @typedef {Object} InternalFile
  * @property {string} id - The ID of the internal file.
  * @property {string} name - The name of the internal file.
  * @property {string} type - The type of the internal file.
  * @property {string} source - The source code of the internal file.
  */

  /**
   * Gets all the internal files of a Google Apps Script file.
   *
   * @returns {InternalFile[]} An array of objects.
   */
  static getScriptInternalFiles(file) {
    // Check that the file is a Google Apps Script file
    if (file.getMimeType() == 'application/vnd.google-apps.script') {
      // Get the script content as a string
      const fileId = file.getId();
      const params = {
        headers: { 
          Authorization: 'Bearer ' + ScriptApp.getOAuthToken(),
          'Accept-Charset': 'utf-8'
        },
        followRedirects: true,
        muteHttpExceptions: true,
      };
      const url =
        'https://script.google.com/feeds/download/export?id='
        + fileId
        + '&format=json';
      const response = UrlFetchApp.fetch(url, params);
      const json = JSON.parse(response);
      return json.files;  
    } else {
      throw new Error("The file is not a Google Apps Script file.");
    }
  }

...而remoteFile來自這里:

  /**
  * @typedef {Object} RepositoryFile
  * @property {string} name - The name of the file.
  * @property {string} path - The file path in the repository.
  * @property {string} sha - The SHA hash of the file.
  * @property {Number} size - The size of the file in bytes.
  * @property {string} url - The URL of the file's contents.
  * @property {string} html_url - The URL to the file's page on the repository website.
  * @property {string} git_url - The URL to the file's git contents.
  * @property {string} download_url - The URL to download the file.
  * @property {string} type - The type of the file, usually "file".
  */

  /**
   * Gets all the internal files of a Google Apps Script file.
   *
   * @returns {RepositoryFile[]} An array of objects.
   */
  listFilesInRepository(repositoryName) {
    let repositoryFiles = [];
    try {
      const options = {
        headers: {
          ...this._authorizationHeader,
        },
      };      
      const response = UrlFetchApp.fetch(`${this._buildRepositoryUrl(repositoryName)}/contents`, options);
      repositoryFiles = JSON.parse(response);
    } catch(e) {
      const errorMessage = GitHubApi._getErrorMessage(e);
      if(errorMessage === 'This repository is empty.') {
        // do nothing
      } else {
        // unknown error
        throw e;
      }
    }
    return repositoryFiles;
  }

...和 SHA 計算:

class GitHubCrypto {
  /**
   * @param {string} fileContent
   * @returns {string} SHA1 hash string
   */
  static getSha(fileContent) {
    // GitHub is computing the sum of `blob <length>\x00<contents>`, where `length` is the length in bytes of the content string and `\x00` is a single null byte.
    // For the Sha1 implementation, see: www.movable-type.co.uk/scripts/sha1.html
    const sha = Sha1.hash('blob ' + fileContent.length + '\x00' + fileContent);
    return sha;
  }
}

我相信你的目標如下。

  • 您想要使用 Google Apps 腳本從ñèàü的值中檢索430f370909821443112064cd149a4bebd271bfc4的 has 值。

在這種情況下,下面的示例腳本怎么樣?

示例腳本:

function myFunction() {
  const fileContent = 'ñèàü'; // This is from your comment.

  const value = 'blob ' + fileContent.length + '\x00' + fileContent;
  const bytes = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_1, value, Utilities.Charset.UTF_8);
  const res = bytes.map(byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
  console.log(res); // 430f370909821443112064cd149a4bebd271bfc4
}
  • 當這個腳本運行時,得到的 has 值為430f370909821443112064cd149a4bebd271bfc4
  • aaaa直接使用時像const res = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_1, "aaaa", Utilities.Charset.UTF_8).map(byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('') ,得到70c881d4a26984ddce795f6f71817c9cf4480e79

參考:

我終於找到了問題......問題出在“字節長度”:

GitHub 正在計算blob <length>\x00<contents>的總和,其中length是內容字符串的字節長度,而\x00是單個 null 字節。

當沒有特殊字符時, lengthInBytes == inputStr.length ,但當有特殊字符時,這不再是真的:

// For the Sha1 implementation, see: www.movable-type.co.uk/scripts/sha1.html

function simpleShaTest1() {
  const fileContent = 'ñèàü';
  const expectedSha = '56c9357fcf2589619880e1978deb8365454ece11';
  const sha = Sha1.hash('blob ' + byteLength(fileContent) + '\x00' + fileContent);
  const areEqual = (sha === expectedSha); // true
}

function simpleShaTest2() {
  const fileContent = 'aaa';
  const expectedSha = '7c4a013e52c76442ab80ee5572399a30373600a2';
  const sha = Sha1.hash('blob ' + lengthInUtf8Bytes(fileContent) + '\x00' + fileContent);
  const areEqual = (sha === expectedSha); // true
}

function lengthInUtf8Bytes(str) {
  // Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence.
  var m = encodeURIComponent(str).match(/%[89ABab]/g);
  // `m` is `null` when there are no special characters, thus returning `m.length`
  return str.length + (m ? m.length : 0);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM