Python: GIT commit programmatically - python

I try to make git commits programmatically right in Python/Django. The problem I encounter is the syntax of the command. The message of a commit is a variable, that may contain several words. So, I tried to do it this way:
command('git commit -m "%s"'%msg) # command is a custom function that calls
# system Popen function
If I then make a push command to a remote repository at github, then first what I see, is that commit messages are in double quotes like "Test" and another problem is that if a message contains two or more words like "Test message", then it is not even executed. In other words, in a situation like
msg = "Test message"
command('git commit -m "%s"'%msg)
Nothing happens.

I used git from command line, too. This is how I do it here.
import subprocess
def git_commit(message):
return subprocess.check_output(['git', 'commit', '-m', message])
If nothing happens it might be that you need to add the changed files first.
def git_add(file_path):
return subprocess.check_output(['git', 'add', file_path])
Or you add and commit all.
def git_commit_all(message):
return subprocess.check_output(['git', 'commit', '-am', message])

Related

How to use GitPython to perform "git push" when using SSH Keys?

I am trying to write a Python Controller, which would help me automate Git -usage. I've gotten all other commands to work - but I am having difficulties with git push equivalent, when using GitPython Library.
This is where I am right now. This should be working without the SSH Key identification, but I have to squeeze that in.
""" Execute Git Push with GitPython Library.
Hardcoded values: 'branch' environment.
TODO: This is not working. """
def push(self, repo_path, branch, commit_message, user):
repo = Repo(repo_path)
repo.git.add('--all')
repo.git.commit('-m', commit_message)
origin = repo.remote(name=branch)
origin.push()
This is what I have on my Initialization. (Cleared some values due to privacy.)
load_dotenv()
self.BRANCH = "TBD" # Hardcoded Value
self.REPO_PATH = os.getenv('REPO_PATH')
self.REPO = Repo(self.REPO_PATH)
self.COMMIT_MESSAGE = '"Commit from Controller."'
# TODO: These should be changed, when deployed.
self.GIT_SSH_KEY = os.path.expanduser('/home/user/.ssh/id_rsa')
self.GIT_SSH_CMD = "ssh -i %s" % self.GIT_SSH_KEY
self.GIT_USER = "user" # This needs to be changed.
From my understanding from this (GitPython and SSH Keys?) the tactic here is to use GIT_SSH environment variable to provide executable, which will call the ssh - but since I am a beginner, I am having trouble understanding what exactly that environment variable should contain, and how to wrap that with the push function.
Thank you in advance!
First, setting values on self isn't going to accomplish anything by itself, unless there are parts of your code you're not showing us. If you need to set the GIT_SSH environment variable, then you would need to set os.environ['GIT_SSH'].
In general, you shouldn't need to set GIT_SSH unless you require a non-default ssh commandline. That is, if I have:
$ git remote -v
origin ssh://git#github.com/larsks/gnu-hello (fetch)
origin ssh://git#github.com/larsks/gnu-hello (push)
Then I can write:
>>> import git
>>> repo = git.Repo('.')
>>> origin = repo.remote('origin')
>>> res = origin.push()
>>> res[0].summary
'[up to date]\n'
I didn't have to set anything special here; the defaults were entirely appropriate. Under the hood, GitPython just calls the git command line, so anything that works with the cli should work fine without special configuration.

Discord.py: How to get the exact word of the command from user input message?

When a user sends a command message in discord, we always get the content of the user without the exact name of the command that they use for doing this command.
For example:
#bot.command(aliases=['bonjour','hola','nihao'])
async def hello(ctx):
# do something
But, because there are aliases, users can use "hello", "bonjour", "hola", or "nihao" for calling this command. So, is there any way to get which one the user actually types for calling this command?
What I have tried (don't work):
ctx.command will always give me "hello" even if I call this command with an alias.
ctx.command.aliases will return me a list of aliases
So, this problem bothers me a lot. Any suggestion or help would be great!
You can use ctx.invoked_with
"The command name that triggered this invocation. Useful for finding out which alias called the command.
Docs: discord.ext.commands.Context.invoked_with
You can check the alias used from the message content available as ctx.message.content.
Something like the following should give you the exact alias
alias = ctx.message.content.lstrip('prefix').split()[0]
Note that you'll need to substitute your bot's actual prefix and that this will only work for commands that consist of a single word (though it can be adapted to work with longer commands as well)

Iterate commits b/w 2 specified commits in GitPython

import git
repo = git.Repo(repo_dir)
ref_name = 'master'
for commit in repo.iter_commits(rev=ref_name):
<some code here>
This code iterates through all the commits. I want to iterate b/w 2 commits.
Just like git log commit1...commit2
How can I do the same using GitPython's iter_commits() method.
repo.iter_commits(rev='1234abc..5678def') works for me in GitPython==2.1.11
Example:
repo = git.Repo(repo_dir)
for commit in repo.iter_commits(rev='master..HEAD'):
<some code here>
You can use pure gitpython for that.
If you want to able to traverse certain commit (assuming the first
commit is HEAD), just use max_count. See The Commit object
two_commits = list(repo.iter_commits('master', max_count=2))
assert len(two_commits) == 2
if you want similar ability to git log commit1...commit2 as you mentioned:
logs = repo.git.log("--oneline", "f5035ce..f63d26b")
will give you:
>>> logs
'f63d26b Fix urxvt name to match debian repo\n571f449 Add more key for helm-org-rifle\nbea2697 Drop bm package'
You can also use logs = repo.git.log("f5035ce..f63d26b") but it will give you all info (just like you use git log without --oneline)
if you want nice output, use pretty print:
from pprint import pprint as pp
>>> pp(logs)
('f63d26b Fix urxvt name to match debian repo\n'
'571f449 Add more key for helm-org-rifle\n'
'bea2697 Drop bm package')
For more explanation about repo.git.log, see https://stackoverflow.com/a/55545500/6000005
I would suggest you to use PyDriller (a wrapper around GitPython, to make things easier). What you asked can be done like this:
for commit in RepositoryMining("path_to_repo", from_commit="first", to_commit="second").traverse_commits():
# your code
First, make a function to run the git command.
from git import *
from subprocess import Popen, PIPE
def execute_gitcmd(cmd, repo):
pipe = subprocess.Popen(cmd, shell=True, cwd=repo, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(out, error) = pipe.communicate()
return out, error
pipe.wait()
Then write any git command as you use on terminal, for example:
gitcmd = "git log -n1 --oneline"
Finally, call your function:
log = (execute_gitcmd(gitcmd, your_repository))
Hope this can help.

How to get full command executed using sh module?

I ran into an error while executing one of our devops scripts. The script uses the sh package (for executing common unix commands, pypi link). However, the commands that are executed are truncated in the messages printed by sh. How can I see the whole command that was executed?
example:
import sh
sh.ssh(host,
'rsync -av {src} {dst}'.format(src=src,
dst=dst),
_out=sys.stdout
)
Produces output like:
INFO:sh.command:<Command '/bin/ssh dbw#ny...(77 more)' call_args {'bg': False, 'timeo...(522 more)>: starting process
I'd like to see the full command executed, and all of the call_args.
sh.ssh returns an sh.RunningCommand object, which you can query to find the call args and the cmd:
import sh
a=sh.ssh(host,
'rsync -av {src} {dst}'.format(src=src,
dst=dst),
_out=sys.stdout
)
print(a.cmd)
print(a.call_args)
After peeking into the source code, it looks like this is controlled by the max_len parameter of the friendly_truncate function, so one option may be to edit the sh.py code directly and set a higher int value:
https://github.com/amoffat/sh/blob/master/sh.py#L424
https://github.com/amoffat/sh/blob/master/sh.py#L425
Or, possibly just remove points where that function is called.

Run mutiple gerrit queries in python

I am trying to run a gerrit cherry pick query in python
query_to_run='git fetch https://gerritserver.com/projectname refs/changes/51/1151/1 ' + '&&' + ' git cherry-pick FETCH_HEAD'
I am getting error:
fatal: Couldn't find remote ref &&
Unexpected end of command stream
My code works with other gerrit queries but not this one, is it the && which is causing problem!
thanks
Pratibha
The && token has no meaning to Git or Gerrit but is interpreted by your shell. By default the subprocess module doesn't pass off commands to the shell but runs the process directly, so the string in query_to_run is sent as a single command. To force subprocess.Popen(), subprocess.check_call() or whatever you're using to pass the command to a shell, pass shell=True:
subprocess.check_call(query_to_run, shell=True)
However, the use of shell=True is discouraged and is unnecessary in this case. What && does is simply run one command and, if successful, run another command. It's basically equivalent to this sequence of Python statements:
subprocess.check_call(command1)
subprocess.check_call(command2)
Alternatively, if you prefer not have exceptions thrown when either of the commands fail:
subprocess.call(command1) != 0 and subprocess.call(command2) != 0
In addition to this, I strongly recommend making a good habit out of passing lists of arguments to process execution functions instead of strings. Passing strings works fine a lot of the time, but when arguments contain spaces you suddenly need to think about quoting.
Putting everything together, this is what I think your code should look like:
try:
subprocess.check_call(['git', 'fetch',
'https://gerritserver.com/projectname',
'refs/changes/51/1151/1'])
subprocess.check_call(['git', 'cherry-pick', 'FETCH_HEAD'])
except (EnvironmentError, subprocess.CalledProcessError):
# Suitable error handling here. I'm not sure about
# the possibility of EnvironmentError exceptions.
Also, a note on terminology: You're talking about Gerrit queries, but using that language might confuse people. By Gerrit query one usually means the Lucene query string entered into the search box in the UI (or the equivalent REST API).

Categories

Resources