Поиск самого старого коммита в репозитории GitHub через API

Каков наиболее эффективный способ определить, когда была сделана первоначальная фиксация в репозитории GitHub? Репозитории имеют created_at свойство, но для репозиториев, которые содержат импортированную историю, самый старый коммит может быть значительно старше.

При использовании командной строки что-то вроде этого будет работать:

git rev-list --max-parents=0 HEAD

Однако я не вижу эквивалента в GitHub API.

6 ответов

Решение

Это можно сделать всего за два запроса, если данные уже кэшированы (на стороне GitHub) и в зависимости от ваших требований к точности.

Сначала проверьте, есть ли на самом деле коммиты до времени создания, выполнив GET за /repos/:owner/:repo/commits с until параметр, установленный на время создания (как предложено в ответе VonC) и ограничивающий число, возвращаемое до 1 коммита (через per_page параметр).

Если есть коммиты до времени создания, то конечная точка статистики участников (/repos/:owner/:repo/stats/contributors) может быть вызван. Ответ имеет weeks список на каждого участника и самый старый w значение там совпадает с самой старой фиксацией.

Если вам нужна точная временная метка, вы можете снова использовать конечную точку списка коммитов с помощью until а также since установите значение 7 дней после самой старой недели.

Обратите внимание, что конечная точка статистики может возвращать 202 указывает на то, что статистика недоступна, и в этом случае требуется повторная попытка через несколько секунд.

Используя GraphQL API, существует обходной путь для получения самой старой фиксации (начальной фиксации) в определенной ветке.

Сначала получите последнюю фиксацию и вернитеtotalCount и endCursor:

{
  repository(name: "linux", owner: "torvalds") {
    ref(qualifiedName: "master") {
      target {
        ... on Commit {
          history(first: 1) {
            nodes {
              message
              committedDate
              authoredDate
              oid
              author {
                email
                name
              }
            }
            totalCount
            pageInfo {
              endCursor
            }
          }
        }
      }
    }
  }
}

Он возвращает что-то подобное для курсора и pageInfo объект:

"totalCount": 931886,
"pageInfo": {
  "endCursor": "b961f8dc8976c091180839f4483d67b7c2ca2578 0"
}

У меня нет источника о формате строки курсора b961f8dc8976c091180839f4483d67b7c2ca2578 0 но я тестировал с другим репозиторием с более чем 1000 коммитов, и кажется, что он всегда форматируется следующим образом:

<static hash> <incremented_number>

Итак, вы должны просто вычесть 2 из totalCount (если totalCount > 1) и получите самую старую фиксацию (или начальную фиксацию, если хотите):

{
  repository(name: "linux", owner: "torvalds") {
    ref(qualifiedName: "master") {
      target {
        ... on Commit {
          history(first: 1, after: "b961f8dc8976c091180839f4483d67b7c2ca2578 931884") {
            nodes {
              message
              committedDate
              authoredDate
              oid
              author {
                email
                name
              }
            }
            totalCount
            pageInfo {
              endCursor
            }
          }
        }
      }
    }
  }
}

что дает следующий результат (начальная фиксация Линуса Торвальдса):

{
  "data": {
    "repository": {
      "ref": {
        "target": {
          "history": {
            "nodes": [
              {
                "message": "Linux-2.6.12-rc2\n\nInitial git repository build. I'm not bothering with the full history,\neven though we have it. We can create a separate \"historical\" git\narchive of that later if we want to, and in the meantime it's about\n3.2GB when imported into git - space that would just make the early\ngit days unnecessarily complicated, when we don't have a lot of good\ninfrastructure for it.\n\nLet it rip!",
                "committedDate": "2005-04-16T22:20:36Z",
                "authoredDate": "2005-04-16T22:20:36Z",
                "oid": "1da177e4c3f41524e886b7f1b8a0c1fc7321cac2",
                "author": {
                  "email": "torvalds@ppc970.osdl.org",
                  "name": "Linus Torvalds"
                }
              }
            ],
            "totalCount": 931886,
            "pageInfo": {
              "endCursor": "b961f8dc8976c091180839f4483d67b7c2ca2578 931885"
            }
          }
        }
      }
    }
  }
}

Простая реализация на python для получения первой фиксации с помощью этого метода:

import requests

token = "YOUR_TOKEN"

name = "linux"
owner = "torvalds"
branch = "master"

query = """
query ($name: String!, $owner: String!, $branch: String!){
  repository(name: $name, owner: $owner) {
    ref(qualifiedName: $branch) {
      target {
        ... on Commit {
          history(first: 1, after: %s) {
            nodes {
              message
              committedDate
              authoredDate
              oid
              author {
                email
                name
              }
            }
            totalCount
            pageInfo {
              endCursor
            }
          }
        }
      }
    }
  }
}
"""

def getHistory(cursor):
    r = requests.post("https://api.github.com/graphql",
        headers = {
            "Authorization": f"Bearer {token}"
        },
        json = {
            "query": query % cursor,
            "variables": {
                "name": name,
                "owner": owner,
                "branch": branch
            }
        })
    return r.json()["data"]["repository"]["ref"]["target"]["history"]

#in the first request, cursor is null
history = getHistory("null")
totalCount = history["totalCount"]
if (totalCount > 1):
    cursor = history["pageInfo"]["endCursor"].split(" ")
    cursor[1] = str(totalCount - 2)
    history = getHistory(f"\"{' '.join(cursor)}\"")
    print(history["nodes"][0])
else:
    print("got oldest commit (initial commit)")
    print(history["nodes"][0])

Вы можете найти пример в javascript в этом посте

Одним из предложений будет перечисление коммитов на репо (см. Раздел GitHub api V3), используя until параметр, задающий создание репо (плюс один день, например).

GET /repos/:owner/:repo/commits

Таким образом, вы бы перечислили все коммиты, созданные во время создания репо или раньше: это ограничит список, исключая все коммиты, созданные после создания репо.

Публикую свое решение, так как все остальные мне не помогли.

Следующий скрипт извлекает список коммитов для данного REPO («владелец/репо»), при необходимости переходит к последней странице и выводит объект JSON последнего (самого старого) коммита.

          REPO="owner/repo"
    URL="https://api.github.com/repos/$REPO/commits"
    H=" -H \"Accept: application/vnd.github+json\" \
      -H \"X-GitHub-Api-Version: 2022-11-28\""
    
    response=$(curl -s -L --include $H $URL | awk 'NR > 1')
    
    # Split the output into header and json
    header=$(echo "$response" | awk 'BEGIN{RS="\r\n";ORS="\r\n"} /^[a-zA-Z0-9-]+:/')
    commits=$(echo "$response" | awk '!/^[a-zA-Z0-9-]+:/')
    
    # If paginated, get last page
    if [[ $header == *"link"* ]]; then
      # Extract the last page value
      link_line=$(echo "$header" | grep -i "^link:")
      last_page=$(echo "$link_line" | sed -n 's/.*page=\([0-9]\+\)[^0-9].*rel="last".*/\1/p')
    
      # Get last-page commits
      commits=$(curl -s -L $H $URL?page=$last_page)
    fi
    
    # Print first commit
    echo $commits | jq '.[-1].commit'

Метод проб и ошибок на странице номер,

https://github.com/fatfreecrm/fat_free_crm/commits/master?page=126

История git, возможно, с использованием, например, gitk, поможет повысить эффективность проб и ошибок.

Это происходит не через API, а на GitHub.com: если у вас есть последний SHA фиксации и счетчик фиксации, вы можете создать URL-адрес, чтобы найти его:

https://github.com/USER/REPO/commits?after=LAST_COMMIT_SHA+COMMIT_COUNT_MINUS_2

# Example. Commit count in this case was 1573
https://github.com/sindresorhus/refined-github/commits/master
  ?after=a76ed868a84cd0078d8423999faaba7380b0df1b+1571
Другие вопросы по тегам