Calling Git Binary From Python And Error Codes - python

I am trying to build a set of git utilities with python. I am using subprocess.Popen to run the git binary. Right now I am just trying to find the best way to determine that there was an error in running a git command.
My question is whether or not git will always return a returncode of 0 on a successful git command and always return a returncode of non-zero on a unsuccessful call? I just want to make sure that checking the returncode is a safe way to detect an error so that I can exit the script if a git command was unsuccessful.

Yes, git (and any well-behaved *nix program) will always return 0 for success and non-zero for failure. This is the paradigm on GNU/Linux systems, and since Git was made by the same person who made Linux, you can bet it follows the convention.

Related

Check latest commit with Python in Linux/Windows/Mac

I'm trying to generate a simple Python code that:
checks if it is running inside a git folder
if so, fetch the latest commit, else skip
it should work under the three platforms: Linux, Windows, and Mac
I have this code that works correctly under Linux:
from subprocess import call, STDOUT
import os
if call(["git", "branch"], stderr=STDOUT, stdout=open(os.devnull, 'w')) != 0:
# Not a git folder
commit = ''
else:
# Inside a git folder.: fetch latest commit
commit = subprocess.check_output(['git', 'rev-parse', '{}'.format('HEAD')])
print(commit)
but I have no way of checking if itwill work under Windows and Mac.
Does it work? Is there any way of checking/knowing this sort of things when one has no access to the other operating system?
You don't want to run git branch to detect whether you're in a Git repository, because you may or may not have any branches. To detect whether you're able to use Git commands, you'll want to run something like git rev-parse --git-dir, which will exit non-zero if you're not within a Git repository.
However, there are a couple of other issues with your code. First of all, in a new repository (one created fresh with git init), there will be a .git directory and the above command will succeed, but HEAD will not point anywhere. Therefore, your git rev-parse HEAD command will fail and print HEAD and an error.
Finally, if you want parse a revision, you should usually use --verify so that you don't print the dummy HEAD value on failure. So your invocation should look like git rev-parse --verify HEAD.
Ultimately, it's up to you to figure out what you want to do in a newly initialized repository, whether that's fail or fall back to an empty string.
The behaviors I've described here are consistent across platforms; they're built into Git and well defined.
There's a method check_output in subprocess library
from subprocess import check_output
try:
# use python to parse this log for info. This is your entire last commit
logs = check_output(['git', 'log', '-1', '--stat']).decode("UTF-8")
except Exception as e:
# Do whatever you wanna do otherwise if not git repository
print(e)
Git has a command called "git log".
"-1" indicates the last commit and
--stat will give you the files that were changed, commit ID, TIME ETC
then you can use python to parse this log and retrive any information you want
Check this out for more info on git log

How do I revert a commit with gitpython?

For a python script I'm writing (using Python 2.7 on Windows 7) I should be able to modify a branch with a given commit, that is adding it (cherry pick) if the commit is missing, or reverting it if it's already present.
Apparently revert has not been wrapped in gitpython's Repo class, so I tried to use Git directly with:
repo.git.revert(reference)
where reference is one of the commits returned by repo.iter_commits("master")
What happens is that the script locks on that command and becomes idle; I then have to kill the command prompt window.
If I go in the working directory and explore the repository, I can see (with git diff) that after the execution, the changes have been applied even tho' no new commit is visibile if I git log.
Any ideas about if and what I'm doing wrong?
I solved the mistery by trying to git commit the applied changes manually. Git complained about a swap file in the working directory.
So, the problem was that the command was being executed as if it was run from a terminal, hence waiting for me to somehow edit the commit message! So I needed to run the revert command with the no-edit option.
I changed the method invocation to:
repo.git.revert(reference.hexsha, no_edit = True)
(notice that gitpython requires the underscore as a separator. Also, using explicitly the hexsha property is not required, since reference would be converted to its str() representation anyway.)
It seems to work.

Rebasing Git Repo with Python

Is there a way to use use python to rebase a repo from one on github, then push the result. As well as detecting if the rebase failed as a result of conflicts that need to be resolved?
Git is primarily a command-line tool. Once installed, you should be able to open-up a console, command prompt, powershell, c-shell, bash shell, etc. and just type git and get a list of available git commands.
Once you have Git setup and working this way, then from Python it would be possible to execute git commands in the same way you would execute any other shell commands. I'm not a Python expert, but ElpieKay suggests in the comments to use:
commands.getstatusoutput("git <command>")
You will need to do a separate search for git rebase specifically and figure out how the output is formatted and parse it to determine success, or possibly there is an error code or StdErr output that you can get through the .getstatusoutput or a similar command from commands in python.
Another thing that may help is looking at the man page for rebase with git rebase --help.
Summary
I recommend doing a search to find out more about the python commands library or just shell interaction in general for python, and then a separate set of searches/research to determine exactly how to implement the git rebase commands and its output format to determine what you need to parse to determine success or failure.

python symlink in windows 10 creators update

Since the windows 10 creator update, you can enable developer mode to circumvent administrator privileges when creating a symlink. Now, I was able to create a symlink using mklink like this:
os.system('mklink %s %s' %(dst, src))
Hopefully it's obvious that dst is the destination symlink path, and src is the source file for the symlink. While it seems to work ok, it doesn't error if it fails which makes it a little more difficult to ensure each symlink is successful. I can check if the path exists after each symlink, but that's less efficient than a try/except clause. There's also what looks like a command shell window(?) that pops up and closes quickly every time - and that's really annoying when you're symlinking a lot of files...
So, I've been trying other options I've found on stack overflow like this one: How to create symlinks in windows using Python? Unfortunately, the CreateSymbolicLinkW command doesn't seem to work for me... I also found this: OS.symlink support in windows where it appears you need to adjust the group policy editor; however, it apparently still requires users in the administrator group to run the process as an administrator even if you explicitly set that user with symlink privileges.
With the windows 10 creator update, there's mention of a new dwflag in the CreateSymbolicLink api (SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) you can see the reference for that here: symlinks windows 10
Using the ctypes stuff is a bit over my head, so I'm wondering if anyone knows: Can I actually use that new dwflag? How do I use it? Will it work without running the process as administrator?
I use Autodesk Maya, so I'm stuck with python 2.7 options... I have not tried launching Maya as an administrator so I don't know if that will work, but it seems like a rather annoying hoop to jump through even if it does... I appreciate any assistance you can give
it doesn't error if it fails
os.system will return the exit status of the call. It does not raise an exception.
If you look at the docs for os.system, they recommend using the subprocess module. In fact, subprocess.check_call does what you describe (raise an exception on a non-zero exit status). Perhaps that would work better.
On the other hand, the command mklink will return a zero exit status even if the source does not exist (it will create a link to non-existent file and return 0). You might want to validate the actual link as you mentioned, depending on what errors you are trying to find.
As far as hiding the console window, see this.
os.symlink works out of the box since python 3.8 on windows, as long as Developer Mode is turned on.
Not sure whether this will help with Maya; they seem to have committed to Python 3 though.

Git push-to-deploy post-receive python script not cannot set env var

I am stuck since 2 days trying to set up a small automatic deployment script.
The thing is: I have been using Git for some months now, but I always used it locally just by myself, just with the purpose of easily saving version of my code. All good until here.
Now I have to find a way to "publish" the code as soon as new functionalities are implemented and I think the code is stable enough.
Searching around I've discovered these 'hooks', which are scripts that are executed by Git in certain situations. Basically the idea is to have my master branch sync'd with my published code, so that everytime I merge a branch to the master and 'push', the files are automatically copied into '/my/published/folder'.
That said, I've found this tutorial that explains to do exactly what I want using a 'hooks' post-receive script, which is written in Ruby. Since at my studio I don't have and don't want to use Ruby at this time, I've found a Python version of the same script.
I tested and tested, but I couldn't make it work. I keep getting the same error:
remote: GIT_WORK_TREE is not recognized as as internal or external command,
Consider this is based on the tutorial I've shared above. Same prj name, same structure, etc.
I even installed Ruby on my personal laptop and tried the original script, but it still doesn't work...
I'm using Windows, and the Git env variable is set and accessible. But nevertheless it seems like it's not recognizing the GIT_WORK_TREE command. If I run it from the Git Bash it works just fine, but if I use the Windows Shell I get the same error message.
I suppose that when in my py script use the call() function, it runs the cmd using the Windows Shell. That's my guess, but I don't really know how to solve it. Google didn't help, as if no one ever had this problem before.
Maybe I'm just not seeing something obvious here, but I spent the whole day on this and I cannot get out of this bog!
Does anyone know how to solve it, or at least have an idea for a workaround?
Hope someone can help...
Thanks a lot!
The Ruby script you are talking about generates "bash" command:
GIT_WORK_TREE=/deploy/path git checkout -f ...
It means: define environment variable "GIT_WORK_TREE" with value "/deploy/path" and execute "git checkout -f ...".
As I understand it doesn't work for Windows command line.
Try to use something like:
set GIT_WORK_TREE=c:\temp\deploy && git checkout -f ...
I've had this problem as well - the best solution I've found is to pass the working tree across as one of the parameters
git --work-tree="/deploy/path" checkout -f ...

Categories