API Github: recupera tutti i commit per tutti i rami per un repo

Secondo la documentazione V2, è ansible elencare tutti i commit per un ramo con:

commits/list/:user_id/:repository/:branch 

Non vedo la stessa funzionalità nella documentazione V3.

Vorrei raccogliere tutti i rami usando qualcosa come:

 https://api.github.com/repos/:user/:repo/branches 

E poi scorrere attraverso di loro, tirando tutti i commit per ciascuno. In alternativa, se c’è un modo per richiamare direttamente tutti i commit per tutti i rami per un repo, ciò funzionerebbe altrettanto bene se non meglio. Qualche idea?

AGGIORNAMENTO: ho provato a passare il ramo: sha come param come segue:

     params = {:page => 1, :per_page => 100, :sha => b} 

    Il problema è che quando lo faccio, non visualizza correttamente i risultati. Mi sembra che ci stiamo avvicinando in modo errato. qualche idea?

    Ho incontrato lo stesso identico problema. Sono riuscito ad acquisire tutti i commit per tutte le filiali all’interno di un repository (probabilmente non efficiente a causa dell’API).

    Approccio per recuperare tutti i commit per tutti i rami in un repository

    Come hai detto, prima raccogli tutti i rami:

     # https://api.github.com/repos/:user/:repo/branches https://api.github.com/repos/twitter/bootstrap/branches 

    La chiave mancante è che APIv3 per ottenere i commit opera utilizzando un commit di riferimento (il parametro per la chiamata API all’elenco si impegna su un repository sha ). Quindi è necessario assicurarsi che quando si raccolgono i rami si raccolga anche il loro ultimo sha:

    Risultato troncato della chiamata API ramo per twitter / bootstrap

     [ { "commit": { "url": "https://api.github.com/repos/twitter/bootstrap/commits/8b19016c3bec59acb74d95a50efce70af2117382", "sha": "8b19016c3bec59acb74d95a50efce70af2117382" }, "name": "gh-pages" }, { "commit": { "url": "https://api.github.com/repos/twitter/bootstrap/commits/d335adf644b213a5ebc9cee3f37f781ad55194ef", "sha": "d335adf644b213a5ebc9cee3f37f781ad55194ef" }, "name": "master" } ] 

    Lavorare con l’ultimo commit

    Quindi, visto che i due rami qui hanno sha diverso, questi sono gli ultimi commit su quei rami. Quello che puoi fare ora è iterare attraverso ogni ramo dal loro ultimo sha:

     # With sha parameter of the branch's lastest sha # https://api.github.com/repos/:user/:repo/commits https://api.github.com/repos/twitter/bootstrap/commits?per_page=100&sha=d335adf644b213a5ebc9cee3f37f781ad55194ef 

    Quindi la suddetta chiamata API elencherà gli ultimi 100 commit del ramo master di twitter / bootstrap . Lavorando con l’API devi specificare il prossimo commit per ottenere i successivi 100 commit. Possiamo usare l’ultimo sha del commit (che è 7a8d6b19767a92b1c4ea45d88d4eedc2b29bf1fa usando l’esempio corrente) come input per la prossima chiamata API:

     # Next API call for commits (use the last commit's sha) # https://api.github.com/repos/:user/:repo/commits https://api.github.com/repos/twitter/bootstrap/commits?per_page=100&sha=7a8d6b19767a92b1c4ea45d88d4eedc2b29bf1fa 

    Questo processo viene ripetuto fino a quando l’ultimo sha del commit è uguale al parametro call sha dell’API.

    Succursale successiva

    Questo è per un ramo. Ora si applica lo stesso approccio per l’altro ramo (funziona dal più recente sha).


    C’è un grosso problema con questo approccio … Poiché le filiali condividono alcuni commit identici, vedrai lo stesso commit ripetuto mentre ti sposti su un altro ramo.

    Posso immaginare che c’è un modo molto più efficace per realizzare questo, eppure questo ha funzionato per me.

    Ho fatto questa stessa domanda per il supporto GitHub e loro mi hanno risposto:

    GETing / repos /: owner /: repo / commit dovrebbe fare il trucco. È ansible passare il nome del ramo nel parametro sha . Ad esempio, per ottenere la prima pagina di commit dal ramo “3.0.0-wip” del repository twitter / bootstrap , si utilizzerà la seguente richiesta di ricciolo:

     curl https://api.github.com/repos/twitter/bootstrap/commits?sha=3.0.0-wip 

    I documenti descrivono anche come utilizzare l’impaginazione per ottenere i commit rimanenti per questo ramo.

    Finché si effettuano richieste autenticate , è ansible effettuare fino a 5.000 richieste all’ora .

    Ho usato i binari github-api nella mia app come segue (usando https://github.com/peter-murach/github gem):

     github_connection = Github.new :client_id => 'your_id', :client_secret => 'your_secret', :oauth_token => 'your_oath_token' branches_info = {} all_branches = git_connection.repos.list_branches owner,repo_name all_branches.body.each do |branch| branches_info["#{branch.name}".to_s] = "#{branch.commit.url}" end branches_info.keys.each do |branch| commits_list.push (git_connection.repos.commits.list owner,repo_name, start_date, end_date, :sha => "branch_name") end 

    Utilizzo dell’API GraphQL v4

    È ansible utilizzare GraphQL API v4 per ottimizzare il download di commit per filiale. Nel seguente metodo, sono riuscito a scaricare in una singola richiesta 1900 commit (100 commit per ramo in 19 rami diversi) che riduce drasticamente il numero di richieste (rispetto all’utilizzo di API REST).

    1 – Ottieni tutti i rami

    Dovrai ottenere tutti i rami e passare all’impaginazione se hai più di 100 filiali:

    Domanda:

     query($owner:String!, $name:String!, $branchCursor: String!) { repository(owner: $owner, name: $name) { refs(first: 100, refPrefix: "refs/heads/",after: $branchCursor) { totalCount edges { node { name target { ...on Commit { history(first:0){ totalCount } } } } } pageInfo { endCursor hasNextPage } } } } 

    variabili:

     { "owner": "google", "name": "gson", "branchCursor": "" } 

    Provalo nell’esploratore

    Tieni presente che la variabile branchCursor viene utilizzata quando hai più di 100 filiali e presenta il valore di pageInfo.endCursor nella richiesta precedente in quel caso.

    2 – Dividere l’array di rami in un array di 19 rami max

    Esiste una limitazione del numero di richieste per nodo che ci impedisce di fare troppe query per nodo. Qui, alcuni test che ho eseguito hanno dimostrato che non possiamo superare 19 * 100 commit in una singola query.

    Si noti che in caso di pronti contro termine che hanno <19 rami, non è necessario preoccuparsi di questo

    3 – La query viene eseguita da un blocco di 100 per ogni ramo

    È quindi ansible creare la query in modo dinamico per ottenere i 100 commit successivi su tutti i rami. Un esempio con 2 rami:

     query ($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { branch0: ref(qualifiedName: "JsonArrayImplementsList") { target { ... on Commit { history(first: 100) { ...CommitFragment } } } } branch1: ref(qualifiedName: "master") { target { ... on Commit { history(first: 100) { ...CommitFragment } } } } } } fragment CommitFragment on CommitHistoryConnection { totalCount nodes { oid message committedDate author { name email } } pageInfo { hasNextPage endCursor } } 

    Provalo nell’esploratore

    • Le variabili utilizzate sono owner del proprietario e del nome del repository per il nome del repository.
    • Un frammento per evitare la duplicazione della definizione del campo cronologia commit.

    Puoi vedere che la pageInfo.hasNextpage e pageInfo.endCursor saranno utilizzati per passare attraverso l’impaginazione per ogni ramo. L’impaginazione avviene nella history(first: 100) con la specifica dell’ultimo cursore incontrato. Ad esempio la prossima richiesta avrà la history(first: 100, after: "6e2fcdcaf252c54a151ce6a4441280e4c54153ae 99") . Per ogni ramo, dobbiamo aggiornare la richiesta con l’ultimo valore endCursor per interrogare per il prossimo 100 commit.

    Quando pageInfo.hasNextPage è false , non c’è più alcuna pagina per questo ramo, quindi non lo includeremo nella prossima richiesta.

    Quando l’ultimo ramo ha pageInfo.hasNextPage su false , abbiamo recuperato tutti i commit

    Esempio di implementazione

    Ecco un’implementazione di esempio in NodeJS utilizzando github-graphql-client . Lo stesso metodo potrebbe essere implementato in qualsiasi altra lingua. Quanto segue memorizzerà anche i commit in un file commitsX.json :

     var client = require('github-graphql-client'); var fs = require("fs"); const owner = "google"; const repo = "gson"; const accessToken = "YOUR_ACCESS_TOKEN"; const branchQuery = ` query($owner:String!, $name:String!, $branchCursor: String!) { repository(owner: $owner, name: $name) { refs(first: 100, refPrefix: "refs/heads/",after: $branchCursor) { totalCount edges { node { name target { ...on Commit { history(first:0){ totalCount } } } } } pageInfo { endCursor hasNextPage } } } }`; function buildCommitQuery(branches){ var query = ` query ($owner: String!, $name: String!) { repository(owner: $owner, name: $name) {`; for (var key in branches) { if (branches.hasOwnProperty(key) && branches[key].hasNextPage) { query+=` ${key}: ref(qualifiedName: "${branches[key].name}") { target { ... on Commit { history(first: 100, after: ${branches[key].cursor ? '"' + branches[key].cursor + '"': null}) { ...CommitFragment } } } }`; } } query+=` } }`; query+= commitFragment; return query; } const commitFragment = ` fragment CommitFragment on CommitHistoryConnection { totalCount nodes { oid message committedDate author { name email } } pageInfo { hasNextPage endCursor } }`; function doRequest(query, variables) { return new Promise(function (resolve, reject) { client({ token: accessToken, query: query, variables: variables }, function (err, res) { if (!err) { resolve(res); } else { console.log(JSON.stringify(err, null, 2)); reject(err); } }); }); } function buildBranchObject(branch){ var refs = {}; for (var i = 0; i < branch.length; i++) { console.log("branch " + branch[i].node.name); refs["branch" + i] = { name: branch[i].node.name, totalCount: branch[i].node.target.history.totalCount, cursor: null, hasNextPage : true, commits: [] }; } return refs; } async function requestGraphql() { var iterateBranch = true; var branches = []; var cursor = ""; // get all branches while (iterateBranch) { let res = await doRequest(branchQuery,{ "owner": owner, "name": repo, "branchCursor": cursor }); iterateBranch = res.data.repository.refs.pageInfo.hasNextPage; cursor = res.data.repository.refs.pageInfo.endCursor; branches = branches.concat(res.data.repository.refs.edges); } //split the branch array into smaller array of 19 items var refChunk = [], size = 19; while (branches.length > 0){ refChunk.push(branches.splice(0, size)); } for (var j = 0; j < refChunk.length; j++) { //1) store branches in a format that makes it easy to concat commit when receiving the query result var refs = buildBranchObject(refChunk[j]); //2) query commits while there are some pages existing. Note that branches that don't have pages are not //added in subsequent request. When there are no more page, the loop exit var hasNextPage = true; var count = 0; while (hasNextPage) { var commitQuery = buildCommitQuery(refs); console.log("request : " + count); let commitResult = await doRequest(commitQuery, { "owner": owner, "name": repo }); hasNextPage = false; for (var key in refs) { if (refs.hasOwnProperty(key) && commitResult.data.repository[key]) { isEmpty = false; let history = commitResult.data.repository[key].target.history; refs[key].commits = refs[key].commits.concat(history.nodes); refs[key].cursor = (history.pageInfo.hasNextPage) ? history.pageInfo.endCursor : ''; refs[key].hasNextPage = history.pageInfo.hasNextPage; console.log(key + " : " + refs[key].commits.length + "/" + refs[key].totalCount + " : " + refs[key].hasNextPage + " : " + refs[key].cursor + " : " + refs[key].name); if (refs[key].hasNextPage){ hasNextPage = true; } } } count++; console.log("------------------------------------"); } for (var key in refs) { if (refs.hasOwnProperty(key)) { console.log(refs[key].totalCount + " : " + refs[key].commits.length + " : " + refs[key].name); } } //3) write commits chunk (up to 19 branches) in a single json file fs.writeFile("commits" + j + ".json", JSON.stringify(refs, null, 4), "utf8", function(err){ if (err){ console.log(err); } console.log("done"); }); } } requestGraphql(); 

    Questo funziona anche con il repo con molti rami, per esempio questo con oltre 700 filiali

    Limite di velocità

    Si noti che mentre con GraphQL è ansible eseguire un numero ridotto di richieste, non necessariamente migliorerà il limite di velocità in quanto il limite di velocità si basa sui punti e non su un numero limitato di richieste: controllare il limite di frequenza dell'API GraphQL