GitPython: получить список удаленных коммитов, еще не примененных - PullRequest
2 голосов
/ 28 ноября 2011

Я пишу скрипт Python, чтобы получить список коммитов, которые собираются применить операция git pull. Отличная библиотека GitPython - отличная база для начала, но тонкая внутренняя работа git убивает меня. Теперь вот что у меня есть (упрощенная и аннотированная версия):

repo = git.Repo(path)                           # get the local repo
local_commit = repo.commit()                    # latest local commit 
remote = git.remote.Remote(repo, 'origin')      # remote repo
info = remote.fetch()[0]                        # fetch changes
remote_commit = info.commit                     # latest remote commit
if local_commit.hexsha == remote_commit.hexsha: # local is updated; end
  return
                                                # for every remote commit
while remote_commit.hexsha != local_commit.hexsha:
  authors.append(remote_commit.author.email)    # note the author
  remote_commit = remote_commit.parents[0]      # navigate up to the parent

По сути, он получает авторов для всех коммитов, которые будут применены в следующем git pull. Это работает хорошо, но у него есть следующие проблемы:

  • Когда локальный коммит опережает удаленный, мой код просто печатает все коммиты до первого.
  • Удаленный коммит может иметь более одного родителя, а локальный коммит может быть вторым родителем. Это означает, что мой код никогда не найдет локальный коммит в удаленном репозитории.

Я могу справиться с удаленными репозиториями, находящимися за локальным: просто посмотрите в другом направлении (локально-удаленно) в то же время, код становится грязным, но он работает. Но эта последняя проблема убивает меня: теперь мне нужно создать дерево (потенциально неограниченное), чтобы найти соответствие для локальной фиксации. Это не просто теоретически: мое последнее изменение было объединением репо, которое представляет эту проблему, поэтому мой сценарий не работает.

Было бы очень полезно получить упорядоченный список коммитов в удаленном репозитории, например, repo.iter_commits() для локального репо. Но я не нашел в документации , как это сделать. Могу ли я просто получить объект Repo для удаленного хранилища?

Есть ли другой подход, который может привести меня туда, и я использую молоток, чтобы гвоздить винты?

1 Ответ

0 голосов
/ 28 ноября 2011

Я понял, что дерево коммитов всегда было таким: один коммит имеет двух родителей, и у обоих родителей один и тот же родитель. Это означает, что у первого коммита есть два родителя, но только один прародитель.

Так что было несложно написать собственный итератор для обработки коммитов, в том числе расходящихся деревьев. Это выглядит так:

def repo_changes(commit):
  "Iterator over repository changes starting with the given commit."
  number = 0
  next_parent = None
  yield commit                           # return the first commit itself
  while len(commit.parents) > 0:         # iterate
    same_parent(commit.parents)          # check only one grandparent
    for parent in commit.parents:        # go over all parents
      yield parent                       # return each parent
      next_parent = parent               # for the next iteration
    commit = next_parent                 # start again

Функция same_parent() предупреждает, когда есть два родителя и более одного прародителя. Теперь легко выполнить итерацию по незаряженным коммитам:

for commit in repo_changes(remote_commit):
  if commit.hexsha == local_commit.hexsha:
    return
  authors.append(remote_commit.author.email)

Я оставил несколько деталей для ясности. Я никогда не возвращаю больше заранее установленного количества коммитов (20 в моем случае), чтобы избежать перехода в конец репо. Я также заранее проверяю, что локальное репо не опережает удаленное. Помимо этого, он работает отлично! Теперь я могу предупредить всех авторов коммитов о том, что их изменения объединяются.

...