Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for paths in checkout arguments #858

Closed
rbistolfi opened this issue Jan 3, 2019 · 13 comments
Closed

Add support for paths in checkout arguments #858

rbistolfi opened this issue Jan 3, 2019 · 13 comments

Comments

@rbistolfi
Copy link
Contributor

Hi there, would be possible to add support for paths to repository.checkout and friends?
https://libgit2.org/docs/guides/101-samples/#checkout_paths

Thanks!

@imbuedhope
Copy link
Contributor

Fairly certain passing directory= to Repository.checkout does just that.

See: https://www.pygit2.org/working-copy.html?highlight=checkout#pygit2.Repository.checkout

@rbistolfi
Copy link
Contributor Author

rbistolfi commented Jan 3, 2019

Hi @imbuedhope

I think that's different, I am looking for the equivalent to git checkout ... -- path/to/a path/to/b. The directory option seems to create a directory and checking out there instead. In other words, it is using https://github.com/libgit2/libgit2/blob/master/include/git2/checkout.h#L285 when I need https://github.com/libgit2/libgit2/blob/master/include/git2/checkout.h#L272

@imbuedhope
Copy link
Contributor

imbuedhope commented Jan 3, 2019

Ah, yeah that does seem to be the case (in repository.py).

@rbistolfi
Copy link
Contributor Author

I tried passing a StrArray.array to copts.paths but FFI complains about it, it requires a list or dict, if I provide a dict I get a segmentation fault. Sorry I am new to FFI, any ideas about how to correctly initialize copts.paths?

@imbuedhope
Copy link
Contributor

imbuedhope commented Jan 3, 2019

It needs to be of the type git_strarray * from the C libgit2 bindings see the api docs for libgit2 or the source.

You'll have to make the struct with FFI first and then pass it as the value for copts.paths I think (not 100% certain, I haven't messed with FFI much either).

@rbistolfi
Copy link
Contributor Author

Yep, it seems to be the right type:

index e609687..cc1fded 100644
--- a/pygit2/repository.py
+++ b/pygit2/repository.py
@@ -54,7 +54,7 @@ from .ffi import ffi, C
 from .index import Index
 from .remote import RemoteCollection
 from .blame import Blame
-from .utils import to_bytes, is_string
+from .utils import to_bytes, is_string, StrArray
 from .submodule import Submodule
 
 
@@ -204,7 +204,7 @@ class BaseRepository(_Repository):
     # Checkout
     #
     @staticmethod
-    def _checkout_args_to_options(strategy=None, directory=None):
+    def _checkout_args_to_options(strategy=None, directory=None, paths=None):
         # Create the options struct to pass
         copts = ffi.new('git_checkout_options *')
         check_error(C.git_checkout_init_options(copts, 1))
@@ -223,6 +223,10 @@ class BaseRepository(_Repository):
             refs.append(target_dir)
             copts.target_directory = target_dir
 
+        if paths:
+            strarray = StrArray(paths)
+            copts.paths = strarray.array
+
         return copts, refs
 
     def checkout_head(self, **kwargs):
diff --git a/test/test_repository.py b/test/test_repository.py
index 523a907..f87c6dd 100644
--- a/test/test_repository.py
+++ b/test/test_repository.py
@@ -332,6 +332,10 @@ class RepositoryTest_II(utils.RepoTestCase):
         self.repo.checkout(ref_i18n, directory=extra_dir)
         assert not len(os.listdir(extra_dir)) == 0
 
+    def test_checkout_paths(self):
+        ref_i18n = self.repo.lookup_reference('refs/heads/i18n')
+        self.repo.checkout(ref_i18n, paths=['README.rst'])
+
     def test_merge_base(self):
         commit = self.repo.merge_base(
             '5ebeeebb320790caf276b9fc8b24546d63316533',

But the test raises:

        if paths:
            strarray = StrArray(paths)
>           copts.paths = strarray.array
E           TypeError: initializer for ctype 'git_strarray' must be a list or tuple or dict or struct-cdata, not cdata 'git_strarray *'

@imbuedhope
Copy link
Contributor

It looks like git_checkout_options.paths is of type git_strarray, while StrArray.array is a git_strarray *.

Try the following instead?

 if paths:
    strarray = StrArray(paths)
    copts.paths = strarray.array[0]

@rbistolfi
Copy link
Contributor Author

That seems to work sometimes. I get segmentation faults in some runs. I will try to make time for properly investigating the requirements for getting this to work consistently. I will start looking for some examples for safe use of StrArray in the codebase. Thanks a lot for your help.

@imbuedhope
Copy link
Contributor

Looks like the ffi variable will be cleaned up by python when the last reference to it vanishes based on the ffi documentation here.

Since copts is used after _checkout_args_to_options returns the grabage collector and libgit2 are in a race to use the variable, which is probably what causes the segfault.

Probably just need to add it to the refs list that is present in the same function so it doesn't go out of scope before the C bindings are called. So something like this should resolve it, I think.

if paths:
    strarray = StrArray(paths)
    copts.paths = strarray.array[0]
    refs.append(strarray)

@rbistolfi
Copy link
Contributor Author

Good catch, thanks! I will give it a try. I will focus in correctness now, creating a more comprehensive test case.

@rbistolfi
Copy link
Contributor Author

I made some progress. Appending strarray to refs seems to have worked. I also improved the behavior a bit: If paths is provided, HEAD is not updated to the given reference, just like the cli tool. Test now asserts that the path checked out has status GIT_STATUS_INDEX_NEW.

@rbistolfi
Copy link
Contributor Author

Created #859

@rbistolfi
Copy link
Contributor Author

PR has been merged, this is now fixed. Thanks @imbuedhope for your help!

netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this issue Mar 20, 2019
Add test target.

0.28.0 (2019-03-19)
-------------------------

- Upgrade to libgit2 0.28
  `#878 <https://github.com/libgit2/pygit2/issues/878>`_

- Add binary wheels for Linux
  `#793 <https://github.com/libgit2/pygit2/issues/793>`_
  `#869 <https://github.com/libgit2/pygit2/pull/869>`_
  `#874 <https://github.com/libgit2/pygit2/pull/874>`_
  `#875 <https://github.com/libgit2/pygit2/pull/875>`_
  `#883 <https://github.com/libgit2/pygit2/pull/883>`_

- New ``pygit2.Mailmap``, see documentation
  `#804 <https://github.com/libgit2/pygit2/pull/804>`_

- New ``Repository.apply(...)`` wraps ``git_apply(..)``
  `#841 <https://github.com/libgit2/pygit2/issues/841>`_
  `#843 <https://github.com/libgit2/pygit2/pull/843>`_

- Now ``Repository.merge_analysis(...)`` accepts an optional reference parameter
  `#888 <https://github.com/libgit2/pygit2/pull/888>`_
  `#891 <https://github.com/libgit2/pygit2/pull/891>`_

- Now ``Repository.add_worktree(...)`` accepts an optional reference parameter
  `#814 <https://github.com/libgit2/pygit2/issues/814>`_
  `#889 <https://github.com/libgit2/pygit2/pull/889>`_

- Now it's possible to set SSL certificate locations
  `#876 <https://github.com/libgit2/pygit2/issues/876>`_
  `#879 <https://github.com/libgit2/pygit2/pull/879>`_
  `#884 <https://github.com/libgit2/pygit2/pull/884>`_
  `#886 <https://github.com/libgit2/pygit2/pull/886>`_

- Test and documentation improvements
  `#873 <https://github.com/libgit2/pygit2/pull/873>`_
  `#887 <https://github.com/libgit2/pygit2/pull/887>`_

Breaking changes:

- Now ``worktree.path`` returns the path to the worktree directory, not to the
  `.git` file within
  `#803 <https://github.com/libgit2/pygit2/issues/803>`_

- Remove undocumented ``worktree.git_path``
  `#803 <https://github.com/libgit2/pygit2/issues/803>`_


0.27.4 (2019-01-19)
-------------------------

- New ``pygit2.LIBGIT2_VER`` tuple
  `#845 <https://github.com/libgit2/pygit2/issues/845>`_
  `#848 <https://github.com/libgit2/pygit2/pull/848>`_

- New objects now support (in)equality comparison and hash
  `#852 <https://github.com/libgit2/pygit2/issues/852>`_
  `#853 <https://github.com/libgit2/pygit2/pull/853>`_

- New references now support (in)equality comparison
  `#860 <https://github.com/libgit2/pygit2/issues/860>`_
  `#862 <https://github.com/libgit2/pygit2/pull/862>`_

- New ``paths`` optional argument in ``Repository.checkout()``
  `#858 <https://github.com/libgit2/pygit2/issues/858>`_
  `#859 <https://github.com/libgit2/pygit2/pull/859>`_

- Fix speed and windows package regression
  `#849 <https://github.com/libgit2/pygit2/issues/849>`_
  `#857 <https://github.com/libgit2/pygit2/issues/857>`_
  `#851 <https://github.com/libgit2/pygit2/pull/851>`_

- Fix deprecation warning
  `#850 <https://github.com/libgit2/pygit2/pull/850>`_

- Documentation fixes
  `#855 <https://github.com/libgit2/pygit2/pull/855>`_

- Add Python classifiers to setup.py
  `#861 <https://github.com/libgit2/pygit2/pull/861>`_

- Speeding up tests in Travis
  `#854 <https://github.com/libgit2/pygit2/pull/854>`_

Breaking changes:

- Remove deprecated `Reference.get_object()`, use `Reference.peel()` instead


0.27.3 (2018-12-15)
-------------------------

- Move to pytest, drop support for Python 3.3 and cffi 0.x
  `#824 <https://github.com/libgit2/pygit2/issues/824>`_
  `#826 <https://github.com/libgit2/pygit2/pull/826>`_
  `#833 <https://github.com/libgit2/pygit2/pull/833>`_
  `#834 <https://github.com/libgit2/pygit2/pull/834>`_

- New support comparing signatures for (in)equality

- New ``Submodule.head_id``
  `#817 <https://github.com/libgit2/pygit2/pull/817>`_

- New ``Remote.prune(...)``
  `#825 <https://github.com/libgit2/pygit2/pull/825>`_

- New ``pygit2.reference_is_valid_name(...)``
  `#827 <https://github.com/libgit2/pygit2/pull/827>`_

- New ``AlreadyExistsError`` and ``InvalidSpecError``
  `#828 <https://github.com/libgit2/pygit2/issues/828>`_
  `#829 <https://github.com/libgit2/pygit2/pull/829>`_

- New ``Reference.raw_name``, ``Reference.raw_shorthand``, ``Tag.raw_name``,
  ``Tag.raw_message`` and ``DiffFile.raw_path``
  `#840 <https://github.com/libgit2/pygit2/pull/840>`_

- Fix decode error in commit messages and signatures
  `#839 <https://github.com/libgit2/pygit2/issues/839>`_

- Fix, raise error in ``Repository.descendant_of(...)`` if commit doesn't exist
  `#822 <https://github.com/libgit2/pygit2/issues/822>`_
  `#842 <https://github.com/libgit2/pygit2/pull/842>`_

- Documentation fixes
  `#821 <https://github.com/libgit2/pygit2/pull/821>`_

Breaking changes:

- Remove undocumented ``Tag._message``, replaced by ``Tag.raw_message``
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants