Skip to content

Commit ca288d4

Browse files
committed
Merge branch 'revparse'
2 parents a4287f6 + 01ab5b9 commit ca288d4

File tree

14 files changed

+936
-490
lines changed

14 files changed

+936
-490
lines changed

lib/git/db.py

+27-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
"""Module with our own gitdb implementation - it uses the git command"""
1+
"""Module with our own gitdb implementation - it uses the git command"""
2+
from exc import (
3+
GitCommandError,
4+
BadObject
5+
)
6+
27
from gitdb.base import (
38
OInfo,
49
OStream
510
)
611

7-
from gitdb.util import bin_to_hex
8-
12+
from gitdb.util import (
13+
bin_to_hex,
14+
hex_to_bin
15+
)
916
from gitdb.db import GitDB
1017
from gitdb.db import LooseObjectDB
1118

@@ -35,3 +42,20 @@ def stream(self, sha):
3542
t = self._git.stream_object_data(bin_to_hex(sha))
3643
return OStream(*t)
3744

45+
46+
# { Interface
47+
48+
def partial_to_complete_sha_hex(self, partial_hexsha):
49+
""":return: Full binary 20 byte sha from the given partial hexsha
50+
:raise AmbiguousObjectName:
51+
:raise BadObject:
52+
:note: currently we only raise BadObject as git does not communicate
53+
AmbiguousObjects separately"""
54+
try:
55+
hexsha, typename, size = self._git.get_object_header(partial_hexsha)
56+
return hex_to_bin(hexsha)
57+
except (GitCommandError, ValueError):
58+
raise BadObject(partial_hexsha)
59+
# END handle exceptions
60+
61+
#} END interface

lib/git/exc.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
# errors.py
1+
# exc.py
22
# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
33
#
44
# This module is part of GitPython and is released under
55
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66
""" Module containing all exceptions thrown througout the git package, """
77

8+
from gitdb.exc import *
9+
810
class InvalidGitRepositoryError(Exception):
911
""" Thrown if the given repository appears to have an invalid format. """
1012

lib/git/ext/gitdb

Submodule gitdb updated from 6c8721a to ac7d475

lib/git/index/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Initialize the index module"""
1+
"""Initialize the index package"""
22

33
from base import *
44
from typ import *

lib/git/index/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,7 @@ def diff(self, other=diff.Diffable.Index, paths=None, create_patch=False, **kwar
11221122
# item. Handle existing -R flags properly. Transform strings to the object
11231123
# so that we can call diff on it
11241124
if isinstance(other, basestring):
1125-
other = Object.new(self.repo, other)
1125+
other = self.repo.rev_parse(other)
11261126
# END object conversion
11271127

11281128
if isinstance(other, Object):

lib/git/objects/base.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,18 @@ def new(cls, repo, id):
4949
5050
:note: This cannot be a __new__ method as it would always call __init__
5151
with the input id which is not necessarily a binsha."""
52-
hexsha, typename, size = repo.git.get_object_header(id)
53-
inst = get_object_type_by_name(typename)(repo, hex_to_bin(hexsha))
54-
inst.size = size
55-
return inst
52+
return repo.rev_parse(str(id))
53+
54+
@classmethod
55+
def new_from_sha(cls, repo, sha1):
56+
"""
57+
:return: new object instance of a type appropriate to represent the given
58+
binary sha1
59+
:param sha1: 20 byte binary sha1"""
60+
oinfo = repo.odb.info(sha1)
61+
inst = get_object_type_by_name(oinfo.type)(repo, oinfo.binsha)
62+
inst.size = oinfo.size
63+
return inst
5664

5765
def _set_self_from_args_(self, args_dict):
5866
"""Initialize attributes on self from the given dict that was retrieved

lib/git/refs.py

+17-5
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def name(self):
6868
:return:
6969
In case of symbolic references, the shortest assumable name
7070
is the path itself."""
71-
return self.path
71+
return self.path
7272

7373
def _abs_path(self):
7474
return join_path_native(self.repo.git_dir, self.path)
@@ -109,6 +109,19 @@ def _iter_packed_refs(cls, repo):
109109
# I believe files are closing themselves on destruction, so it is
110110
# alright.
111111

112+
@classmethod
113+
def dereference_recursive(cls, repo, ref_path):
114+
"""
115+
:return: hexsha stored in the reference at the given ref_path, recursively dereferencing all
116+
intermediate references as required
117+
:param repo: the repository containing the reference at ref_path"""
118+
while True:
119+
ref = cls(repo, ref_path)
120+
hexsha, ref_path = ref._get_ref_info()
121+
if hexsha is not None:
122+
return hexsha
123+
# END recursive dereferencing
124+
112125
def _get_ref_info(self):
113126
"""Return: (sha, target_ref_path) if available, the sha the file at
114127
rela_path points to, or None. target_ref_path is the reference we
@@ -195,9 +208,8 @@ def _set_reference(self, ref):
195208
try:
196209
write_value = ref.commit.hexsha
197210
except AttributeError:
198-
sha = str(ref)
199211
try:
200-
obj = Object.new(self.repo, sha)
212+
obj = self.repo.rev_parse(ref+"^{}") # optionally deref tags
201213
if obj.type != "commit":
202214
raise TypeError("Invalid object type behind sha: %s" % sha)
203215
write_value = obj.hexsha
@@ -333,7 +345,7 @@ def _create(cls, repo, path, resolve, reference, force):
333345
# figure out target data
334346
target = reference
335347
if resolve:
336-
target = Object.new(repo, reference)
348+
target = repo.rev_parse(str(reference))
337349

338350
if not force and isfile(abs_ref_path):
339351
target_data = str(target)
@@ -523,7 +535,7 @@ def _get_object(self):
523535
always point to the actual object as it gets re-created on each query"""
524536
# have to be dynamic here as we may be a tag which can point to anything
525537
# Our path will be resolved to the hexsha which will be used accordingly
526-
return Object.new(self.repo, self.path)
538+
return Object.new_from_sha(self.repo, hex_to_bin(self.dereference_recursive(self.repo, self.path)))
527539

528540
def _set_object(self, ref):
529541
"""

lib/git/remote.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ def _from_line(cls, repo, line, fetch_line):
391391
split_token = '...'
392392
if control_character == ' ':
393393
split_token = split_token[:-1]
394-
old_commit = Commit.new(repo, operation.split(split_token)[0])
394+
old_commit = repo.rev_parse(operation.split(split_token)[0])
395395
# END handle refspec
396396
# END reference flag handling
397397

lib/git/repo/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Initialize the Repo package"""
2+
3+
from base import *

lib/git/repo.py renamed to lib/git/repo/base.py

+39-55
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,39 @@
44
# This module is part of GitPython and is released under
55
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66

7-
from exc import InvalidGitRepositoryError, NoSuchPathError
8-
from cmd import Git
9-
from objects import Actor
10-
from refs import *
11-
from index import IndexFile
12-
from objects import *
13-
from config import GitConfigParser
14-
from remote import Remote
15-
16-
from db import (
7+
from git.exc import InvalidGitRepositoryError, NoSuchPathError
8+
from git.cmd import Git
9+
from git.objects import Actor
10+
from git.refs import *
11+
from git.index import IndexFile
12+
from git.objects import *
13+
from git.config import GitConfigParser
14+
from git.remote import Remote
15+
from git.db import (
1716
GitCmdObjectDB,
1817
GitDB
1918
)
2019

20+
2121
from gitdb.util import (
2222
join,
23-
isdir,
2423
isfile,
25-
join,
2624
hex_to_bin
2725
)
26+
27+
from fun import (
28+
rev_parse,
29+
is_git_dir,
30+
touch
31+
)
32+
2833
import os
2934
import sys
3035
import re
3136

3237

3338
__all__ = ('Repo', )
3439

35-
def touch(filename):
36-
fp = open(filename, "a")
37-
fp.close()
38-
39-
def is_git_dir(d):
40-
""" This is taken from the git setup.c:is_git_directory
41-
function."""
42-
43-
if isdir(d) and \
44-
isdir(join(d, 'objects')) and \
45-
isdir(join(d, 'refs')):
46-
headref = join(d, 'HEAD')
47-
return isfile(headref) or \
48-
(os.path.islink(headref) and
49-
os.readlink(headref).startswith('refs'))
50-
return False
51-
5240

5341
class Repo(object):
5442
"""Represents a git repository and allows you to query references,
@@ -70,6 +58,7 @@ class Repo(object):
7058
# precompiled regex
7159
re_whitespace = re.compile(r'\s+')
7260
re_hexsha_only = re.compile('^[0-9A-Fa-f]{40}$')
61+
re_hexsha_shortened = re.compile('^[0-9A-Fa-f]{4,40}$')
7362
re_author_committer_start = re.compile(r'^(author|committer)')
7463
re_tab_full_line = re.compile(r'^\t(.*)$')
7564

@@ -109,7 +98,7 @@ def __init__(self, path=None, odbt = GitDB):
10998
self.git_dir = curpath
11099
self._working_tree_dir = os.path.dirname(curpath)
111100
break
112-
gitpath = os.path.join(curpath, '.git')
101+
gitpath = join(curpath, '.git')
113102
if is_git_dir(gitpath):
114103
self.git_dir = gitpath
115104
self._working_tree_dir = curpath
@@ -139,7 +128,7 @@ def __init__(self, path=None, odbt = GitDB):
139128
self.git = Git(self.working_dir)
140129

141130
# special handling, in special times
142-
args = [os.path.join(self.git_dir, 'objects')]
131+
args = [join(self.git_dir, 'objects')]
143132
if issubclass(odbt, GitCmdObjectDB):
144133
args.append(self.git)
145134
self.odb = odbt(*args)
@@ -160,11 +149,11 @@ def __repr__(self):
160149

161150
# Description property
162151
def _get_description(self):
163-
filename = os.path.join(self.git_dir, 'description')
152+
filename = join(self.git_dir, 'description')
164153
return file(filename).read().rstrip()
165154

166155
def _set_description(self, descr):
167-
filename = os.path.join(self.git_dir, 'description')
156+
filename = join(self.git_dir, 'description')
168157
file(filename, 'w').write(descr+'\n')
169158

170159
description = property(_get_description, _set_description,
@@ -334,11 +323,9 @@ def commit(self, rev=None):
334323
:param rev: revision specifier, see git-rev-parse for viable options.
335324
:return: ``git.Commit``"""
336325
if rev is None:
337-
rev = self.active_branch
338-
339-
c = Object.new(self, rev)
340-
assert c.type == "commit", "Revision %s did not point to a commit, but to %s" % (rev, c)
341-
return c
326+
return self.active_branch.commit
327+
else:
328+
return self.rev_parse(str(rev)+"^0")
342329

343330
def iter_trees(self, *args, **kwargs):
344331
""":return: Iterator yielding Tree objects
@@ -359,14 +346,9 @@ def tree(self, rev=None):
359346
it cannot know about its path relative to the repository root and subsequent
360347
operations might have unexpected results."""
361348
if rev is None:
362-
rev = self.active_branch
363-
364-
c = Object.new(self, rev)
365-
if c.type == "commit":
366-
return c.tree
367-
elif c.type == "tree":
368-
return c
369-
raise ValueError( "Revision %s did not point to a treeish, but to %s" % (rev, c))
349+
return self.active_branch.commit.tree
350+
else:
351+
return self.rev_parse(str(rev)+"^{tree}")
370352

371353
def iter_commits(self, rev=None, paths='', **kwargs):
372354
"""A list of Commit objects representing the history of a given ref/commit
@@ -393,11 +375,11 @@ def iter_commits(self, rev=None, paths='', **kwargs):
393375
return Commit.iter_items(self, rev, paths, **kwargs)
394376

395377
def _get_daemon_export(self):
396-
filename = os.path.join(self.git_dir, self.DAEMON_EXPORT_FILE)
378+
filename = join(self.git_dir, self.DAEMON_EXPORT_FILE)
397379
return os.path.exists(filename)
398380

399381
def _set_daemon_export(self, value):
400-
filename = os.path.join(self.git_dir, self.DAEMON_EXPORT_FILE)
382+
filename = join(self.git_dir, self.DAEMON_EXPORT_FILE)
401383
fileexists = os.path.exists(filename)
402384
if value and not fileexists:
403385
touch(filename)
@@ -413,7 +395,7 @@ def _get_alternates(self):
413395
"""The list of alternates for this repo from which objects can be retrieved
414396
415397
:return: list of strings being pathnames of alternates"""
416-
alternates_path = os.path.join(self.git_dir, 'objects', 'info', 'alternates')
398+
alternates_path = join(self.git_dir, 'objects', 'info', 'alternates')
417399

418400
if os.path.exists(alternates_path):
419401
try:
@@ -436,9 +418,9 @@ def _set_alternates(self, alts):
436418
:note:
437419
The method does not check for the existance of the paths in alts
438420
as the caller is responsible."""
439-
alternates_path = os.path.join(self.git_dir, 'objects', 'info', 'alternates')
421+
alternates_path = join(self.git_dir, 'objects', 'info', 'alternates')
440422
if not alts:
441-
if os.path.isfile(alternates_path):
423+
if isfile(alternates_path):
442424
os.remove(alternates_path)
443425
else:
444426
try:
@@ -466,7 +448,7 @@ def is_dirty(self, index=True, working_tree=True, untracked_files=False):
466448
default_args = ('--abbrev=40', '--full-index', '--raw')
467449
if index:
468450
# diff index against HEAD
469-
if os.path.isfile(self.index.path) and self.head.is_valid() and \
451+
if isfile(self.index.path) and self.head.is_valid() and \
470452
len(self.git.diff('HEAD', '--cached', *default_args)):
471453
return True
472454
# END index handling
@@ -674,7 +656,7 @@ def clone(self, path, **kwargs):
674656
# our git command could have a different working dir than our actual
675657
# environment, hence we prepend its working dir if required
676658
if not os.path.isabs(path) and self.git.working_dir:
677-
path = os.path.join(self.git._working_dir, path)
659+
path = join(self.git._working_dir, path)
678660
return Repo(os.path.abspath(path), odbt = odbt)
679661

680662

@@ -693,11 +675,13 @@ def archive(self, ostream, treeish=None, prefix=None, **kwargs):
693675
if treeish is None:
694676
treeish = self.active_branch
695677
if prefix and 'prefix' not in kwargs:
696-
kwargs['prefix'] = prefix
678+
kwargs['prefix'] = prefix
697679
kwargs['output_stream'] = ostream
698680

699681
self.git.archive(treeish, **kwargs)
700682
return self
701-
683+
684+
rev_parse = rev_parse
685+
702686
def __repr__(self):
703687
return '<git.Repo "%s">' % self.git_dir

0 commit comments

Comments
 (0)