Як я можу назвати `` git pull '' з Python?


79

Використовуючи веб-хуки github, я хотів би мати можливість внести будь-які зміни на віддалений сервер розробки. На даний момент, коли знаходиться у відповідному каталозі, git pullотримує будь-які зміни, які потрібно внести. Однак я не можу зрозуміти, як викликати цю функцію з Python. Я спробував наступне:

import subprocess
process = subprocess.Popen("git pull", stdout=subprocess.PIPE)
output = process.communicate()[0]

Але це призводить до наступної помилки

Відстеження (останній останній дзвінок): Файл "", рядок 1, у файлі "/usr/lib/python2.7/subprocess.py", рядок 679, init errread, errwrite) Файл "/ usr / lib / python2. 7 / subprocess.py ", рядок 1249, в _execute_child підняти child_exception OSError: [Errno 2] Немає такого файлу або каталогу

Чи є спосіб, як я можу викликати цю команду bash зсередини Python?



4
@Brandon це неправда, є багато інших рішень, найкраще.
jleahy

1
Чи gitвиконуваний файл у PATH?
тикати

1
Можливо, @jleahy, наскільки я розумію, celecnius ефективно запитує "як мені запустити команду bash". Про це багато разів запитували і відповідали.
ceptno

1
Це старе запитання, але я думаю, що subprocess.Popenзараз є cwdаргумент ключового слова, який буде виконувати команду у вказаному каталозі. Напр .:subprocess.Popen(['git', 'pull', '-v', 'origin', 'master'], cwd='/path/to/git/repo', ...)
Горобець1029

Відповіді:


145

Ви розглядали можливість використання GitPython? Він призначений для того, щоб впоратись із цією нісенітницею.

import git 

g = git.cmd.Git(git_dir)
g.pull()

https://github.com/gitpython-developers/GitPython


7
git_dir - це будь-який каталог, керований вами джерелом (який містить папку .git)
Меркурій

Він запитує пароль .. Як я можу передати пароль для цього ??
Раві Шенкер Редді

Ви можете використовувати ssh для встановлення ключів, тому автентифікація пароля не потрібна
acaruci

msg = g.pull()розповідає, що сталося. msgє ''або , 'Updating ... Fast-forward README.md | 1 + 1 file changed, 1 insertion(+)'якщо ви витягли успішно і 'Already up-to-date.'якщо ви ... вже в курсі.
гіпотеза

3
Це дійсно поганий спосіб використання GitPython - він обробляє ледве будь-яке сміття і просто перекладається subprocess.check_output(['git', 'pull'], cwd=git_dir), залишаючи вам самостійно аналізувати висновок команди "git porcelain", чого ви не повинні робити
Ерік

49

subprocess.Popenочікує переліку назви програми та аргументів. Ви передаєте йому один рядок, який (за замовчуванням shell=False) еквівалентний:

['git pull']

Це означає, що підпроцес намагається знайти програму, названу буквально git pull, і не робить цього: У Python 3.3 ваш код викликає виняток FileNotFoundError: [Errno 2] No such file or directory: 'git pull'. Натомість передайте такий список:

import subprocess
process = subprocess.Popen(["git", "pull"], stdout=subprocess.PIPE)
output = process.communicate()[0]

До речі, в Python 2.7+ ви можете спростити цей код за допомогою check_outputфункції зручності:

import subprocess
output = subprocess.check_output(["git", "pull"])

Крім того, щоб використовувати функціональність git, ні в якому разі не потрібно (хоча і просто і портативно) викликати git двійковим файлом. Подумайте про використання git-python або Dulwich .


За документацією: "аргументи повинні бути послідовністю аргументів програми, або ж окремим рядком ." (наголос мій). Ви впевнені в цьому, бо це працює для мене?
тикати

@poke Так, документація трохи незрозуміла. Якщо ви хочете передати рядок, ви повинні перейти shell=True.
phihag

Ну, як я вже сказав, мені це вдається subprocess.Popen("git pull", stdout=subprocess.PIPE).communicate()(3.3 та 2.7 у Windows). - Ну, читаючи трохи далі, документи пояснюють, що це буде працювати на Unix лише тоді, коли не буде передано жодних аргументів .. Неважливо тоді ^^
тикайте

1
@poke Ви мали рацію щодо одного рядка, хоча моє початкове пояснення було помилковим. Виявляється, ви можете передати в рядку, але ( цитую ): If args is a string, the interpretation is platform-dependent. Додано більше деталей. У Python 3.3 повідомлення про помилку набагато приємніше.
phihag

Це не працює у вікнах, коли потрібен autocrlf. Виклик git із cmd - це не те саме, що виклик git із sh.exe. Автоматична заміна crfl не враховується, і тому статус git не працює
Tomas Kubes

20

Прийнята відповідь за допомогою GitPython є трохи кращою, ніж просто використання subprocessбезпосередньо.

Проблема цього підходу полягає в тому, що якщо ви хочете проаналізувати результати, ви в кінцевому підсумку дивитесь на результат команди "фарфор", що є поганою ідеєю

Використовувати GitPython таким чином - це все одно, що отримати новий блискучий набір інструментів, а потім використовувати його для купи гвинтів, які тримають його разом, а не інструментів всередині. Ось як API був розроблений для використання:

import git
repo = git.Repo('Path/to/repo')
repo.remotes.origin.pull()

Якщо ви хочете перевірити, чи щось змінилося, можете скористатися

current = repo.head.commit
repo.remotes.origin.pull()
if current != repo.head.commit:
    print("It changed")

3

Це зразок рецепта, який я використовував в одному зі своїх проектів. Погодився, що існує кілька способів це зробити. :)

>>> import subprocess, shlex
>>> git_cmd = 'git status'
>>> kwargs = {}
>>> kwargs['stdout'] = subprocess.PIPE
>>> kwargs['stderr'] = subprocess.PIPE
>>> proc = subprocess.Popen(shlex.split(git_cmd), **kwargs)
>>> (stdout_str, stderr_str) = proc.communicate()
>>> return_code = proc.wait()

>>> print return_code
0

>>> print stdout_str
# On branch dev
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   file1
#   file2
nothing added to commit but untracked files present (use "git add" to track)

>>> print stderr_str

Проблема з вашим кодом полягала в тому, що ви не передавали масив subprocess.Popen()і, отже, намагалися запустити єдиний двійковий файл, що викликається git pull. Натомість йому потрібно виконати двійковий файл gitз першим аргументом pullі так далі.


1
Не те, що splitнасправді повинно бути shlex.split, і цей код без потреби ускладнюється - вам не потрібно waitпісля communicate, а для більшості випадків використання вже check_outputробить правильний рядок.
phihag

Я використовую метод wait () для збору коду повернення процесу для своїх потреб. Це необов’язково, якщо OP не зацікавлений у перевірці коду повернення. shlex.splitПроте домовились про використання . І так, якщо вам не цікаво захоплювати stdout або stderr, ви можете пропустити їх. Немає нічого поганого у наданні альтернативних рішень, що дозволяють робити щось більше замість стандартних прикладів документації до python;)
Tuxdude

І як ви вже згадували, check_outputдоданий лише з 2.7, у мене була необхідність запускати свій сценарій у середовищах із версіями python, старшими за цю.
Tuxdude

Хоча waitпрацює, proc.pidце набагато легше зрозуміти. І оскільки check_outputце чистий Python, його можна просто скопіювати, щоб він був доступний і на старих версіях Python.
phihag

О солодкий! так, check_outputздається, також надійнішим :)
Tuxdude

2

Якщо ви використовуєте Python 3.5+ воліють , subprocess.runщоб subprocess.Popenдля сценаріїв він може обробляти. Наприклад:

import subprocess
subprocess.run(["git", "pull"], check=True, stdout=subprocess.PIPE).stdout

-3

Спробуйте:

subprocess.Popen("git pull", stdout=subprocess.PIPE, shell=True)

3
Це, мабуть, породило б оболонку, чи не так?
phihag
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.