Wie kann ich 'git pull' in Python aufrufen?

77

Mit den Github-Webhooks möchte ich Änderungen an einem Remote-Entwicklungsserver vornehmen können. Im Moment, wenn Sie sich im entsprechenden Verzeichnis befinden, werden git pullalle Änderungen abgerufen, die vorgenommen werden müssen. Ich kann jedoch nicht herausfinden, wie diese Funktion in Python aufgerufen werden kann. Ich habe folgendes versucht:

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

Dies führt jedoch zu folgendem Fehler

Traceback (letzter Aufruf zuletzt): Datei "", Zeile 1, in Datei "/usr/lib/python2.7/subprocess.py", Zeile 679, in init errread, errwrite) Datei "/ usr / lib / python2. 7 / subprocess.py ", Zeile 1249, in _execute_child erhöhen child_exception OSError: [Errno 2] Keine solche Datei oder kein solches Verzeichnis

Gibt es eine Möglichkeit, diesen Bash-Befehl in Python aufzurufen?

djq
quelle
Dies ist ein Duplikat von stackoverflow.com/questions/4256107/…
ceptno
4
@Brandon das stimmt nicht, es gibt viele andere Lösungen, am besten.
Jleahy
1
Ist die gitausführbare Datei im Pfad?
stupsen
1
@jleahy Vielleicht fragt Celecnius nach meinem Verständnis effektiv: "Wie führe ich einen Bash-Befehl aus?" Dies wurde schon oft gefragt und beantwortet.
Ceptno
1
Dies ist eine alte Frage, aber ich denke, subprocess.Popenjetzt gibt es ein cwdSchlüsselwortargument, das den Befehl in einem angegebenen Verzeichnis ausführt. Beispiel:subprocess.Popen(['git', 'pull', '-v', 'origin', 'master'], cwd='/path/to/git/repo', ...)
Sparrow1029

Antworten:

143

Haben Sie überlegt, GitPython zu verwenden? Es wurde entwickelt, um all diesen Unsinn für Sie zu erledigen.

import git 

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

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

jleahy
quelle
7
git_dir ist jedes quellgesteuerte Verzeichnis, das Sie haben (das den Ordner .git enthält)
Mercury
Es wird zur Eingabe eines Passworts aufgefordert. Wie kann ich das Passwort dafür übergeben?
Ravi Shanker Reddy
Sie können ssh verwenden, um Schlüssel einzurichten, so dass keine Passwortauthentifizierung erforderlich ist
acaruci
msg = g.pull()sagt dir was passiert ist. msgist ''oder 'Updating ... Fast-forward README.md | 1 + 1 file changed, 1 insertion(+)'wenn du erfolgreich gezogen hast und 'Already up-to-date.'wenn du ... schon auf dem neuesten Stand bist.
Hypothese
3
Dies ist eine wirklich schlechte Art, GitPython zu verwenden - es behandelt kaum den Müll und übersetzt nur in subprocess.check_output(['git', 'pull'], cwd=git_dir), sodass Sie die Ausgabe eines "git porcelain" -Befehls selbst analysieren können, was Sie nicht tun sollten
Eric
48

subprocess.Popenerwartet eine Liste des Programmnamens und der Argumente. Sie übergeben ihm eine einzelne Zeichenfolge, die (standardmäßig shell=False) entspricht:

['git pull']

Das bedeutet, dass der Unterprozess versucht, ein Programm mit dem wörtlichen Namen zu finden git pull, und dies nicht tut: In Python 3.3 löst Ihr Code die Ausnahme aus FileNotFoundError: [Errno 2] No such file or directory: 'git pull'. Geben Sie stattdessen eine Liste wie folgt ein:

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

Übrigens können Sie in Python 2.7+ diesen Code mit der check_outputpraktischen Funktion vereinfachen :

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

Um die Git-Funktionalität nutzen zu können, ist es keineswegs notwendig (wenn auch einfach und portabel), die Git-Binärdatei aufzurufen. Erwägen Sie die Verwendung von Git-Python oder Dulwich .

Phihag
quelle
Gemäß Dokumentation: "Argumente sollten eine Folge von Programmargumenten oder eine einzelne Zeichenfolge sein ." (Hervorhebung von mir). Bist du dir da sicher, weil es bei mir funktioniert?
stupsen
@poke Ja, die Dokumentation ist etwas unklar. Wenn Sie eine Zeichenfolge übergeben möchten, müssen Sie übergeben shell=True.
Phihag
Nun, wie gesagt, es funktioniert nur für mich subprocess.Popen("git pull", stdout=subprocess.PIPE).communicate()(3.3 und 2.7 unter Windows). - Nun, ein bisschen weiter lesen, erklären die Dokumente, dass dies nur unter Unix funktioniert, wenn keine Argumente übergeben werden. Vergiss dann ^^
stupse den
1
@poke Du hattest Recht mit dem Fall einer einzelnen Zeichenfolge, meine anfängliche Erklärung war falsch. Es stellt sich heraus, dass Sie eine Zeichenfolge übergeben können, aber ( zitiert ) : If args is a string, the interpretation is platform-dependent. Weitere Details hinzugefügt. In Python 3.3 ist die Fehlermeldung viel besser.
Phihag
Dies funktioniert unter Windows nicht, wenn Autocrlf benötigt wird. Das Aufrufen von git von cmd ist nicht dasselbe wie das Aufrufen von git von sh.exe. Der automatische Crfl-Austausch wird nicht berücksichtigt und deshalb funktioniert der Git-Status nicht
Tomas Kubes
19

Die akzeptierte Antwort mit GitPython ist kaum besser als nur subprocessdirekt.

Das Problem bei diesem Ansatz ist, dass Sie, wenn Sie die Ausgabe analysieren möchten, am Ende das Ergebnis eines "Porzellan" -Befehls betrachten, was eine schlechte Idee ist

Wenn Sie GitPython auf diese Weise verwenden, erhalten Sie eine glänzende neue Toolbox und diese dann für den Stapel von Schrauben, die sie zusammenhalten, anstelle der darin enthaltenen Werkzeuge. So wurde die API für die Verwendung entwickelt:

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

Wenn Sie überprüfen möchten, ob sich etwas geändert hat, können Sie verwenden

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

Dies ist ein Beispielrezept, das ich in einem meiner Projekte verwendet habe. Einverstanden, dass es mehrere Möglichkeiten gibt, dies zu tun. :) :)

>>> 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

Das Problem mit Ihrem Code war, dass Sie kein Array für übergeben haben subprocess.Popen()und daher versucht haben, eine einzelne Binärdatei namens aufzurufen git pull. Stattdessen muss die Binärdatei gitmit dem ersten Argument pullusw. ausgeführt werden.

Tuxdude
quelle
1
Nicht, dass splitdas wirklich so sein sollte shlex.split, und dieser Code ist unnötig kompliziert - Sie müssen nicht waitnach communicateund in den meisten Anwendungsfällen check_outputbereits die richtige Zeichenfolge ausführen.
Phihag
Ich verwende wait (), um den Rückkehrcode des Prozesses für meine Bedürfnisse zu erfassen. Dies ist optional, wenn das OP nicht an der Überprüfung des Rückkehrcodes interessiert ist. Einverstanden über die Verwendung shlex.split. Und ja, wenn Sie nicht daran interessiert sind, stdout oder stderr zu greifen, können Sie diese überspringen. Es ist nichts Falsches daran, alternative Lösungen
bereitzustellen, mit
Und wie Sie bereits erwähnt haben, check_outputwurde es erst seit 2.7 hinzugefügt. Ich musste mein Skript auch in Umgebungen mit älteren Python-Versionen ausführen.
Tuxdude
Während der waitArbeit proc.pidist viel einfacher zu verstehen. Und da check_outputes sich um reines Python handelt, kann man es einfach kopieren , um es auch in älteren Python-Versionen verfügbar zu haben.
Phihag
Oh wie süß! ja check_outputscheint auch robuster :)
Tuxdude
1

Wenn Sie Python verwenden 3.5+ bevorzugen , subprocess.runum subprocess.Popenfür Szenarien können damit umgehen. Zum Beispiel:

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

Versuchen:

subprocess.Popen("git pull", stdout=subprocess.PIPE, shell=True)
orip
quelle
3
Dies würde jedoch unnötig eine Muschel hervorbringen, nicht wahr?
Phihag