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
Improved gitattribute handling #2744
Changes from 5 commits
a9d7c68
cbb5520
a493c0b
cf07dc2
ad8ce54
ca1c49b
74a5f0b
996007d
d17d9d9
5591d53
36b15cf
106a49e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
import shlex | ||
import time | ||
import os | ||
import os.path as op | ||
from os import linesep | ||
from os.path import join as opj | ||
from os.path import exists | ||
|
@@ -2319,28 +2320,86 @@ def get_deleted_files(self): | |
for f in self.repo.git.diff('--raw', '--name-status', '--staged').split('\n') | ||
if f.split('\t')[0] == 'D'] | ||
|
||
def get_git_attributes(self): | ||
"""Check git attribute for the current repository (not per-file support for now) | ||
def get_gitattributes(self, path, index_only=False): | ||
"""Query gitattributes for one or more paths | ||
|
||
Parameters | ||
---------- | ||
all_: bool | ||
Adds --all to git check-attr call | ||
path: path or list | ||
Path(s) to query. Paths may be relative or absolute. | ||
index_only: bool | ||
Flag whether to consider only gitattribute setting that are reflected | ||
in the repository index, not just in the work tree content. | ||
|
||
Returns | ||
------- | ||
dict: | ||
attribute: value pairs | ||
Each key is a queried path, each value is a dictionary with attribute | ||
name and value items. Attribute values are either True or False, | ||
for set and unset attributes, or are the literal attribute value. | ||
""" | ||
out, err = self._git_custom_command(["."], ["git", "check-attr", "--all"]) | ||
assert not err, "no stderr output is expected" | ||
out_split = [ | ||
# splitting by : would leave leading space(s) | ||
[e.lstrip(' ') for e in l.split(':', 2)] | ||
for l in out.split('\n') if l | ||
] | ||
assert all(o[0] == '.' for o in out_split) # for paranoid | ||
return dict(o[1:] for o in out_split) | ||
path = assure_list(path) | ||
cmd = ["git", "check-attr", "-z", "--all"] | ||
if index_only: | ||
cmd.append('--cached') | ||
stdout, stderr = self._git_custom_command(path, cmd) | ||
# make sure we have one entry for each query path to | ||
# simplify work with the result | ||
attributes = {_normalize_path(self.path, p): {} for p in path} | ||
attr = [] | ||
for item in stdout.split('\0'): | ||
attr.append(item) | ||
if len(attr) < 3: | ||
continue | ||
# we have a full record | ||
p, name, value = attr | ||
attrs = attributes[p] | ||
attrs[name] = \ | ||
True if value == 'set' else False if value == 'unset' else value | ||
# done, reset item | ||
attr = [] | ||
return attributes | ||
|
||
def set_gitattributes(self, attrs, attrfile='.gitattributes'): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if this one should be named There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is it true that a later value can augment a previous one? IIRC a later value in a .gitattributes file overrides the previous one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kyleam but it is done on a per attribute basis. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yarikoptic I think neither There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm confused. I thought "existing ones" in @yarikoptic's comment referred to attributes. Perhaps I should have read it to mean a spec line. |
||
"""Set gitattributes | ||
|
||
Parameters | ||
---------- | ||
attrs : list | ||
Each item is a 2-tuple, where the first element is a path pattern, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. feels that it is a use-case to use a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would work in the general case, because it is possible to set attributes on a single paths on multiple lines. |
||
and the second element is a dictionary with attribute key/value | ||
pairs. The attribute dictionary must use the same semantics as those | ||
returned by `get_gitattributes()`. Path patterns can use absolute paths, | ||
in which case they will be normalized relative to the directory | ||
that contains the target .gitattribute file (see `attrfile`). | ||
attrfile: path | ||
Path relative to the repository root of the .gitattributes file the | ||
attributes shall be set in. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just curious if you know about a use-case where having it as an option would come handy already with git? i.e. if there is another file which might follow this syntax to be used for storing some attrs? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Any .gitattributes file in a subdirectory? We could assume the base name .gitattributes and change this argument to take a directory, though the current approach seems straightforward enough. We don't use it at the moment, but there is also $GITDIR/info/attributes, though taking that relative to the repo root isn't convenient for that use case since $GITDIR isn't necessarily "root/.git". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, it isn't necessarily always There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks |
||
""" | ||
git_attributes_file = op.join(self.path, attrfile) | ||
attrdir = op.dirname(git_attributes_file) | ||
if not op.exists(attrdir): | ||
os.makedirs(attrdir) | ||
with open(git_attributes_file, 'a') as f: | ||
for pattern, attr in sorted(attrs, key=lambda x: x[0]): | ||
# normalize the pattern relative to the target .gitattributes file | ||
npath = _normalize_path( | ||
op.join(self.path, op.dirname(attrfile)), pattern) | ||
attrline = u'' | ||
if npath.count(' '): | ||
# quote patterns with spaces | ||
attrline += u'"{}"'.format(npath) | ||
else: | ||
attrline += npath | ||
for a in sorted(attr): | ||
val = attr[a] | ||
if val is True: | ||
attrline += ' {}'.format(a) | ||
elif val is False: | ||
attrline += ' -{}'.format(a) | ||
else: | ||
attrline += ' {}={}'.format(a, val) | ||
f.write('{}\n'.format(attrline)) | ||
|
||
|
||
# TODO | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, a single '*' works recursively there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK.