gitpython-developers / GitPython

GitPython is a python library used to interact with Git repositories.

Home Page:http://gitpython.readthedocs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

If `diff.external` is set, diffing via API fails silently as external tool isn't understood

can-taslicukur opened this issue · comments

GitPython version: 3.1.42.

from git import Repo

repo = Repo(".")
print(repo.index.diff("HEAD"))
print(repo.index.diff("HEAD", create_patch=True))

print(repo.index.diff(None))
print(repo.index.diff(None, create_patch=True))

prints

[<git.diff.Diff object at 0x10cbb08b0>]
[]
[<git.diff.Diff object at 0x103d49d30>]
[]

R=True workaround mentioned in #852 does not help either:

print(repo.index.diff("HEAD", create_patch=True, R=True))
# []

This also happens when I try to diff tree against index or working tree

print(repo.head.commit.diff(None, create_patch=True))
print(repo.head.commit.diff(None))

print(repo.head.commit.diff())
print(repo.head.commit.diff(create_patch=True))

returns

[]
[<git.diff.Diff object at 0x103d69670>, <git.diff.Diff object at 0x103d699d0>]
[]
[<git.diff.Diff object at 0x103d69700>]

It looks like using create_patch=True when comparison includes index or working tree always returns empty list. So right now only way to reliably use create_patch=True is to diff tree against tree.

Originally posted by @can-taslicukur in #852 (comment)

Can you give instructions to get the repository to the state where those statements produce those results? I've tried to reproduce this on Ubuntu and Windows, and so far I've been unable to get an empty list with create_patch=True in a situation where it is nonempty without it.

For example, on Ubuntu 22.04 LTS with git 2.34.1 using Python 3.12.1 with GitPython 3.1.42 installed in a virtual environment, in a repository consisting of a single commit of a one-line file to which a second line is appended and staged and a third line is appended and not staged, all the calls you showed gave one-element results, with no zero-element results:

ek@Glub:~/tmp$ mkdir investigate-1828
ek@Glub:~/tmp$ cd investigate-1828/
ek@Glub:~/tmp/investigate-1828$ git init .
Initialized empty Git repository in /home/ek/tmp/investigate-1828/.git/
ek@Glub:~/tmp/investigate-1828 (main #)$ echo .venv >.gitignore
ek@Glub:~/tmp/investigate-1828 (main #%)$ git add .
ek@Glub:~/tmp/investigate-1828 (main +)$ git commit -m 'Initial commit'
[main (root-commit) 66d6bcc] Initial commit
 1 file changed, 1 insertion(+)
 create mode 100644 .gitignore
ek@Glub:~/tmp/investigate-1828 (main)$ echo __pycache__/ >>.gitignore
ek@Glub:~/tmp/investigate-1828 (main *)$ git add .
ek@Glub:~/tmp/investigate-1828 (main +)$ echo '# third line' >>.gitignore
ek@Glub:~/tmp/investigate-1828 (main *+)$ git show
commit 66d6bcc368351bd23f8cea1bb43113ef79110a99 (HEAD -> main)
Author: Eliah Kagan <degeneracypressure@gmail.com>
Date:   Sun Feb 18 22:04:28 2024 -0500

    Initial commit

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1d17dae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.venv
ek@Glub:~/tmp/investigate-1828 (main *+)$ git diff --staged
diff --git a/.gitignore b/.gitignore
index 1d17dae..3367433 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 .venv
+__pycache__/
ek@Glub:~/tmp/investigate-1828 (main *+)$ git diff
diff --git a/.gitignore b/.gitignore
index 3367433..a2b3f2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 .venv
 __pycache__/
+# third line
ek@Glub:~/tmp/investigate-1828 (main *+)$ python3.12 -m venv .venv
ek@Glub:~/tmp/investigate-1828 (main *+)$ . .venv/bin/activate
(.venv) ek@Glub:~/tmp/investigate-1828 (main *+)$ pip install GitPython
Collecting GitPython
  Obtaining dependency information for GitPython from https://files.pythonhosted.org/packages/67/c7/995360c87dd74e27539ccbfecddfb58e08f140d849fcd7f35d2ed1a5f80f/GitPython-3.1.42-py3-none-any.whl.metadata
  Downloading GitPython-3.1.42-py3-none-any.whl.metadata (12 kB)
Collecting gitdb<5,>=4.0.1 (from GitPython)
  Obtaining dependency information for gitdb<5,>=4.0.1 from https://files.pythonhosted.org/packages/fd/5b/8f0c4a5bb9fd491c277c21eff7ccae71b47d43c4446c9d0c6cff2fe8c2c4/gitdb-4.0.11-py3-none-any.whl.metadata
  Using cached gitdb-4.0.11-py3-none-any.whl.metadata (1.2 kB)
Collecting smmap<6,>=3.0.1 (from gitdb<5,>=4.0.1->GitPython)
  Obtaining dependency information for smmap<6,>=3.0.1 from https://files.pythonhosted.org/packages/a7/a5/10f97f73544edcdef54409f1d839f6049a0d79df68adbc1ceb24d1aaca42/smmap-5.0.1-py3-none-any.whl.metadata
  Using cached smmap-5.0.1-py3-none-any.whl.metadata (4.3 kB)
Downloading GitPython-3.1.42-py3-none-any.whl (195 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 195.4/195.4 kB 1.7 MB/s eta 0:00:00
Using cached gitdb-4.0.11-py3-none-any.whl (62 kB)
Using cached smmap-5.0.1-py3-none-any.whl (24 kB)
Installing collected packages: smmap, gitdb, GitPython
Successfully installed GitPython-3.1.42 gitdb-4.0.11 smmap-5.0.1

[notice] A new release of pip is available: 23.2.1 -> 24.0
[notice] To update, run: pip install --upgrade pip
(.venv) ek@Glub:~/tmp/investigate-1828 (main *+)$ python
Python 3.12.1 (main, Dec 10 2023, 15:07:36) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from git import Repo
>>> repo = Repo(".")
>>> repo.index.diff("HEAD")
[<git.diff.Diff object at 0x7f7ae22cacb0>]
>>> repo.index.diff("HEAD", create_patch=True)
[<git.diff.Diff object at 0x7f7ae22cac20>]
>>> repo.index.diff(None)
[<git.diff.Diff object at 0x7f7ae22cab90>]
>>> repo.index.diff(None, create_patch=True)
[<git.diff.Diff object at 0x7f7ae22cacb0>]
>>> repo.index.diff("HEAD", create_patch=True, R=True)
[<git.diff.Diff object at 0x7f7ae22cab00>]
>>> repo.head.commit.diff(None, create_patch=True)
[<git.diff.Diff object at 0x7f7ae22cac20>]
>>> repo.head.commit.diff(None)
[<git.diff.Diff object at 0x7f7ae22cae60>]
>>> repo.head.commit.diff()
[<git.diff.Diff object at 0x7f7ae22cad40>]
>>> repo.head.commit.diff(create_patch=True)
[<git.diff.Diff object at 0x7f7ae22cab00>]

So my guess is that this may only happen under particular conditions, such as when a repository has a particular combination of committed, staged, and unstaged changes, or maybe only with particular versions of Git, of Python, etc.

Hi, thank you so much for the answer!

I am using Python 3.8 and git version 2.39.3 (Apple Git-145) on Macbook Air M2

Here are steps to reproduce:

mkdir investigate-1828
git --version
# git version 2.39.3 (Apple Git-145)
cd investigate-1828/
git init .
# Initialized empty Git repository in /Users/cantaslicukur/investigate-1828/.git/
echo .venv >.gitignore
git add .
git status
# On branch main
#
# No commits yet
#
# Changes to be committed:
#  (use "git rm --cached <file>..." to unstage)
#	new file:   .gitignore

git commit -m 'Initial commit'
# [main (root-commit) 480924f] Initial commit
# 1 file changed, 1 insertion(+)
# create mode 100644 .gitignore

echo __pycache__/ >>.gitignore
git add .

echo '# third line' >>.gitignore

git show
# commit 480924f1dda82f54472d28809db33451deed18ea (HEAD -> main)
# Author: Can Taşlıçukur <can.taslicukur@ozu.edu.tr>
# Date:   Mon Feb 19 17:47:03 2024 +0300
#
#    Initial commit
#
# diff --git a/.gitignore b/.gitignore
# new file mode 100644
# index 0000000..1d17dae
# --- /dev/null
# +++ b/.gitignore
# @@ -0,0 +1 @@

python --version
# Python 3.8.18
python -m venv .venv
. .venv/bin/activate
pip install GitPython
# Collecting GitPython
#   Downloading GitPython-3.1.42-py3-none-any.whl (195 kB)
#      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 195.4/195.4 kB 2.4 MB/s eta 0:00:00
# Collecting gitdb<5,>=4.0.1
#   Downloading gitdb-4.0.11-py3-none-any.whl (62 kB)
#      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.7/62.7 kB 3.2 MB/s eta 0:00:00
# Collecting smmap<6,>=3.0.1
#   Downloading smmap-5.0.1-py3-none-any.whl (24 kB)
# Installing collected packages: smmap, gitdb, GitPython
# Successfully installed GitPython-3.1.42 gitdb-4.0.11 smmap-5.0.1

# [notice] A new release of pip is available: 23.0.1 -> 24.0
# [notice] To update, run: pip install --upgrade pip

python
# Python 3.8.18 | packaged by conda-forge | (default, Dec 23 2023, 17:25:47)
# [Clang 16.0.6 ] on darwin
# Type "help", "copyright", "credits" or "license" for more information.
>>> from git import Repo
>>>
>>> repo = Repo(".")
>>> print(repo.index.diff("HEAD"))
[<git.diff.Diff object at 0x1013a0f70>]
>>> print(repo.index.diff("HEAD", create_patch=True))
[]
>>>
>>> print(repo.index.diff(None))
[<git.diff.Diff object at 0x1013a0e50>]
>>> print(repo.index.diff(None, create_patch=True))
[]
>>> print(repo.head.commit.diff(None, create_patch=True))
[]
>>> print(repo.head.commit.diff(None))
[<git.diff.Diff object at 0x1013a0ca0>]
>>>
>>> print(repo.head.commit.diff())
[<git.diff.Diff object at 0x1013a0dc0>]
>>> print(repo.head.commit.diff(create_patch=True))
[]

Update: I have tried running the steps above with Python 3.12.2 and git version 2.43.2 (installed via brew). I get the same results :(

All right! I found the issue! Good news, it is my fault :D

I just remembered that I use difftastic and in my ~/.gitconfig, I have the following:

[diff]
    external = difft

I have deleted this from my ~/.gitconfig and gitPython works fine!

Great to hear it's resolved!

If you are interested, you could submit a PR with a fix, so such workarounds aren't required anymore. It should be quite easy to override this setting using environment variables when launching the git-diff process from within GitPython.