4

I am trying to replicate the behaviour of the command "git checkout (commit)" where (commit) is the reference to as specific commit and not a branch name.

When using this command, the 'HEAD' of the repository point to the commit (detached head) and the working directory is in the same state that it was in this commit.

For the moment, I managed to make the HEAD of the repository point to a commit with PyGit2 :

def go(self, repo_name, version):
    repo = pygit2.Repository(bdd[repo_name])
    #commit = repo.revparse_single(version)
    #repo.reset(version, pygit2.GIT_RESET_HARD)
    repo.set_head(pygit2.Oid(hex=version))
    print repo.head_is_detached

My problem is that I can't find how to rollback the working directory like the Git CLI does. I tried using:

  • repo.checkout_head() : It doesn't do anything on the working directory.
  • repo.checkout() : Crash with a GitError: X conflicts prevent checkout

Is there a way to replicate this behaviour without using Repository.reset(ref, pygit2.GIT_RESET_HARD) ?

Maeln
  • 370
  • 1
  • 21
  • Is your working directory clean before you try to run `repo.checkout()`? – Nils Werner May 10 '17 at 07:52
  • @NilsWerner I try to do the `repo.checkout()` after I changed the HEAD to point to the commit. At this point the working directory contain everything that was in the last commit made, so the working directory is not clean from the perspective of the HEAD I guess. – Maeln May 10 '17 at 07:57
  • So simply don't combine `set_head()` (it makes workspace dirty) and `checkout()` (expects workspace to be clean). Just use `checkout()`. – Nils Werner May 10 '17 at 07:58
  • @NilsWerner My problem in this case is that `Repository.checkout(ref)` take a reference and don't accept the `Commit` object nor it's hexadecimal representation or the Oid. So I can't find how to `checkout()` to a specific commit in pygit2. – Maeln May 10 '17 at 08:02
  • `repo.checkout(version)` where `version` is a sha1 string doesn't work? – Nils Werner May 10 '17 at 08:07
  • @NilsWerner It doesn't. `Repository.checkout(ref)` will call `lookup_reference(refname)` if the object you given it is neither a `Reference` or `'HEAD'`. If you give it a `Commit` or the commit `Oid` it returns a type error (lookup_reference takes only string) and if you give the SHA1 string of the commit it return `ValueError: (commit string): The given reference name (commit string) is not valid`. – Maeln May 10 '17 at 08:16
  • And how did you manage to get `checkout()` to show the error `GitError: X conflicts prevent checkout`? – Nils Werner May 10 '17 at 08:22
  • You can use `checkout()` without any argument. If you do, it does a `checkout_index()`. It's the `checkout_index()` who really return the `GitError: X conflicts ...`. – Maeln May 10 '17 at 08:30
  • 2
    Low-level checkout is `git read-tree -um HEAD $target && git update-ref HEAD $target`; pygit2 apparently understands only the one-tree read and none of the options on that, so however it's doing checkout and merge and any number of other operations it doesn't offer more than a crude mock-up of actual git. It looks like you could kludge it with add a ref to your commit, check that out, then reset `HEAD` and delete the ref. – jthill May 12 '17 at 15:19
  • @jthill I tried it (create ref with target commit then checkout this ref and checkout 'refs/heads/master' to go back) and it's working perfectly for my use case. Can you put it as an answer so I can validate it ? – Maeln May 15 '17 at 08:25
  • To my understanding of `pygit2` this is close to impossible, so why not just run the original git command directly instead of putting energy into modeling it via pygit2? – Done Data Solutions May 15 '17 at 08:30
  • @rrschmidt I cannot use the git command due to constraint in my project. I am "forced" to use libgit2/pygit2. – Maeln May 15 '17 at 09:05

2 Answers2

3

Low-level checkout is git read-tree -um HEAD $target && git update-ref HEAD $target; pygit2 apparently understands only the one-tree read and none of the options on that, so however it's doing checkout and merge and any number of other operations it doesn't offer more than a crude mock-up of actual git. It looks like you could kludge it with add a ref to your commit, check that out, then reset HEAD and delete the ref.

jthill
  • 55,082
  • 5
  • 77
  • 137
0

repo.checkout_tree can directly use a commit. After it's done, you still need the set_head. Also, pass the following strategy to checkout_tree if you want 'git reset' equivalent: pygit2.GIT_CHECKOUT_FORCE | pygit2.GIT_CHECKOUT_RECREATE_MISSING

Grant Husbands
  • 138
  • 1
  • 6