visidata 2.11.1__py3-none-any.whl → 3.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- visidata/__init__.py +72 -91
- visidata/_input.py +259 -42
- visidata/_open.py +84 -29
- visidata/_types.py +21 -3
- visidata/_urlcache.py +17 -4
- visidata/aggregators.py +78 -25
- visidata/apps/__init__.py +0 -0
- visidata/apps/vdsql/__about__.py +8 -0
- visidata/apps/vdsql/__init__.py +5 -0
- visidata/apps/vdsql/__main__.py +27 -0
- visidata/apps/vdsql/_ibis.py +748 -0
- visidata/apps/vdsql/bigquery.py +61 -0
- visidata/apps/vdsql/clickhouse.py +53 -0
- visidata/apps/vdsql/setup.py +40 -0
- visidata/apps/vdsql/snowflake.py +67 -0
- visidata/apps/vgit/__init__.py +13 -0
- {vgit → visidata/apps/vgit}/blame.py +5 -2
- {vgit → visidata/apps/vgit}/branch.py +31 -16
- {vgit → visidata/apps/vgit}/config.py +3 -3
- visidata/apps/vgit/diff.py +169 -0
- visidata/apps/vgit/gitsheet.py +161 -0
- {vgit → visidata/apps/vgit}/grep.py +6 -5
- visidata/apps/vgit/log.py +81 -0
- {vgit → visidata/apps/vgit}/main.py +18 -5
- {vgit → visidata/apps/vgit}/remote.py +8 -4
- visidata/apps/vgit/repos.py +71 -0
- {vgit → visidata/apps/vgit}/setup.py +6 -4
- visidata/apps/vgit/stash.py +69 -0
- visidata/apps/vgit/status.py +204 -0
- {vgit → visidata/apps/vgit}/statusbar.py +2 -0
- visidata/basesheet.py +63 -51
- visidata/canvas.py +208 -93
- visidata/choose.py +6 -6
- visidata/clean_names.py +29 -0
- visidata/clipboard.py +73 -17
- visidata/cliptext.py +220 -46
- visidata/cmdlog.py +88 -114
- visidata/color.py +142 -56
- visidata/column.py +121 -129
- visidata/ddw/input.ddw +74 -79
- visidata/ddw/regex.ddw +57 -0
- visidata/ddwplay.py +33 -14
- visidata/deprecated.py +77 -3
- visidata/desktop/visidata.desktop +7 -0
- visidata/editor.py +12 -6
- visidata/errors.py +6 -2
- visidata/experimental/__init__.py +0 -0
- visidata/experimental/diff_sheet.py +29 -0
- visidata/experimental/digit_autoedit.py +6 -0
- visidata/experimental/gdrive.py +89 -0
- visidata/experimental/google.py +37 -0
- visidata/experimental/gsheets.py +79 -0
- visidata/experimental/live_search.py +37 -0
- visidata/experimental/liveupdate.py +45 -0
- visidata/experimental/mark.py +133 -0
- visidata/experimental/noahs_tapestry/__init__.py +1 -0
- visidata/experimental/noahs_tapestry/tapestry.py +147 -0
- visidata/experimental/rownum.py +73 -0
- visidata/experimental/slide_cells.py +26 -0
- visidata/expr.py +8 -4
- visidata/extensible.py +22 -4
- visidata/features/__init__.py +0 -0
- visidata/features/addcol_audiometadata.py +42 -0
- visidata/features/addcol_histogram.py +34 -0
- visidata/features/canvas_save_svg.py +69 -0
- visidata/features/change_precision.py +46 -0
- visidata/features/cmdpalette.py +197 -0
- visidata/features/colorbrewer.py +363 -0
- visidata/{colorsheet.py → features/colorsheet.py} +17 -16
- visidata/features/command_server.py +105 -0
- visidata/features/currency_to_usd.py +70 -0
- visidata/{customdate.py → features/customdate.py} +2 -0
- visidata/features/dedupe.py +132 -0
- visidata/{describe.py → features/describe.py} +17 -15
- visidata/features/errors_guide.py +26 -0
- visidata/features/expand_cols.py +202 -0
- visidata/{fill.py → features/fill.py} +3 -1
- visidata/{freeze.py → features/freeze.py} +11 -6
- visidata/features/graph_seaborn.py +79 -0
- visidata/features/helloworld.py +10 -0
- visidata/features/hint_types.py +17 -0
- visidata/{incr.py → features/incr.py} +5 -0
- visidata/{join.py → features/join.py} +107 -53
- visidata/features/known_cols.py +21 -0
- visidata/features/layout.py +62 -0
- visidata/{melt.py → features/melt.py} +32 -21
- visidata/features/normcol.py +118 -0
- visidata/features/open_config.py +7 -0
- visidata/features/open_syspaste.py +18 -0
- visidata/features/ping.py +157 -0
- visidata/features/procmgr.py +208 -0
- visidata/features/random_sample.py +6 -0
- visidata/{regex.py → features/regex.py} +47 -31
- visidata/features/reload_every.py +55 -0
- visidata/features/rename_col_cascade.py +30 -0
- visidata/features/scroll_context.py +60 -0
- visidata/features/select_equal_selected.py +11 -0
- visidata/features/setcol_fake.py +65 -0
- visidata/{slide.py → features/slide.py} +77 -21
- visidata/features/sparkline.py +48 -0
- visidata/features/status_source.py +20 -0
- visidata/{sysedit.py → features/sysedit.py} +2 -1
- visidata/features/sysopen_mailcap.py +46 -0
- visidata/features/term_extras.py +13 -0
- visidata/{transpose.py → features/transpose.py} +5 -4
- visidata/features/type_ipaddr.py +73 -0
- visidata/features/type_url.py +11 -0
- visidata/{unfurl.py → features/unfurl.py} +9 -9
- visidata/{window.py → features/window.py} +2 -2
- visidata/form.py +50 -21
- visidata/freqtbl.py +81 -33
- visidata/fuzzymatch.py +414 -0
- visidata/graph.py +105 -33
- visidata/guide.py +200 -0
- visidata/help.py +75 -44
- visidata/hint.py +39 -0
- visidata/indexsheet.py +109 -0
- visidata/input_history.py +55 -0
- visidata/interface.py +58 -0
- visidata/keys.py +20 -16
- visidata/loaders/__init__.py +9 -0
- visidata/loaders/_pandas.py +61 -21
- visidata/loaders/api_airtable.py +70 -0
- visidata/loaders/api_bitio.py +102 -0
- visidata/loaders/api_matrix.py +148 -0
- visidata/loaders/api_reddit.py +306 -0
- visidata/loaders/api_zulip.py +249 -0
- visidata/loaders/archive.py +41 -7
- visidata/loaders/arrow.py +7 -7
- visidata/loaders/conll.py +49 -0
- visidata/loaders/csv.py +25 -7
- visidata/loaders/eml.py +3 -4
- visidata/loaders/f5log.py +1204 -0
- visidata/loaders/fec.py +325 -0
- visidata/loaders/fixed_width.py +2 -4
- visidata/loaders/frictionless.py +3 -3
- visidata/loaders/geojson.py +8 -5
- visidata/loaders/google.py +48 -0
- visidata/loaders/graphviz.py +4 -4
- visidata/loaders/hdf5.py +4 -4
- visidata/loaders/html.py +54 -12
- visidata/loaders/http.py +84 -30
- visidata/loaders/imap.py +20 -10
- visidata/loaders/jrnl.py +52 -0
- visidata/loaders/json.py +83 -29
- visidata/loaders/jsonla.py +74 -0
- visidata/loaders/lsv.py +15 -11
- visidata/loaders/mailbox.py +40 -0
- visidata/loaders/markdown.py +1 -3
- visidata/loaders/mbtiles.py +4 -5
- visidata/loaders/mysql.py +11 -13
- visidata/loaders/npy.py +7 -7
- visidata/loaders/odf.py +4 -1
- visidata/loaders/orgmode.py +428 -0
- visidata/loaders/pandas_freqtbl.py +14 -20
- visidata/loaders/parquet.py +62 -6
- visidata/loaders/pcap.py +3 -3
- visidata/loaders/pdf.py +4 -3
- visidata/loaders/png.py +19 -13
- visidata/loaders/postgres.py +9 -8
- visidata/loaders/rec.py +7 -3
- visidata/loaders/s3.py +342 -0
- visidata/loaders/sas.py +5 -5
- visidata/loaders/scrape.py +186 -0
- visidata/loaders/shp.py +6 -5
- visidata/loaders/spss.py +5 -6
- visidata/loaders/sqlite.py +68 -28
- visidata/loaders/texttables.py +1 -1
- visidata/loaders/toml.py +60 -0
- visidata/loaders/tsv.py +61 -19
- visidata/loaders/ttf.py +19 -7
- visidata/loaders/unzip_http.py +6 -5
- visidata/loaders/usv.py +1 -1
- visidata/loaders/vcf.py +16 -16
- visidata/loaders/vds.py +10 -7
- visidata/loaders/vdx.py +30 -5
- visidata/loaders/xlsb.py +8 -1
- visidata/loaders/xlsx.py +145 -25
- visidata/loaders/xml.py +6 -3
- visidata/loaders/xword.py +4 -4
- visidata/loaders/yaml.py +15 -5
- visidata/macros.py +129 -42
- visidata/main.py +119 -94
- visidata/mainloop.py +101 -155
- visidata/man/parse_options.py +2 -2
- visidata/man/vd.1 +302 -149
- visidata/man/vd.txt +291 -154
- visidata/memory.py +3 -3
- visidata/menu.py +104 -423
- visidata/metasheets.py +59 -141
- visidata/modify.py +78 -23
- visidata/motd.py +3 -3
- visidata/mouse.py +137 -0
- visidata/movement.py +43 -35
- visidata/optionssheet.py +99 -0
- visidata/path.py +113 -32
- visidata/pivot.py +73 -47
- visidata/plugins.py +65 -192
- visidata/pyobj.py +55 -205
- visidata/rename_col.py +20 -0
- visidata/save.py +37 -20
- visidata/search.py +54 -10
- visidata/selection.py +84 -5
- visidata/settings.py +162 -25
- visidata/sheets.py +239 -260
- visidata/shell.py +51 -21
- visidata/sidebar.py +162 -0
- visidata/sort.py +11 -4
- visidata/statusbar.py +114 -104
- visidata/stored_list.py +43 -0
- visidata/stored_prop.py +38 -0
- visidata/tests/benchmark.csv +52 -0
- visidata/tests/conftest.py +3 -3
- visidata/tests/test_cliptext.py +39 -0
- visidata/tests/test_commands.py +65 -7
- visidata/tests/test_edittext.py +2 -2
- visidata/tests/test_features.py +28 -0
- visidata/tests/test_menu.py +14 -0
- visidata/tests/test_path.py +13 -4
- visidata/text_source.py +53 -0
- visidata/textsheet.py +10 -3
- visidata/theme.py +44 -0
- visidata/themes/__init__.py +0 -0
- visidata/themes/ascii8.py +84 -0
- visidata/themes/asciimono.py +84 -0
- visidata/themes/light.py +17 -0
- visidata/threads.py +89 -40
- visidata/tuiwin.py +22 -0
- visidata/type_currency.py +22 -3
- visidata/type_date.py +31 -9
- visidata/type_floatsi.py +5 -1
- visidata/undo.py +17 -5
- visidata/utils.py +106 -23
- visidata/vdobj.py +28 -17
- visidata/windows.py +10 -0
- visidata/wrappers.py +9 -3
- visidata-3.0.1.data/data/share/applications/visidata.desktop +7 -0
- {visidata-2.11.1.data → visidata-3.0.1.data}/data/share/man/man1/vd.1 +302 -149
- {visidata-2.11.1.data → visidata-3.0.1.data}/data/share/man/man1/visidata.1 +302 -149
- visidata-3.0.1.data/scripts/vd2to3.vdx +9 -0
- {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/METADATA +12 -8
- visidata-3.0.1.dist-info/RECORD +258 -0
- {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/WHEEL +1 -1
- vgit/__init__.py +0 -1
- vgit/gitsheet.py +0 -164
- visidata/layout.py +0 -44
- visidata/misc.py +0 -5
- visidata-2.11.1.data/scripts/vgit +0 -9
- visidata-2.11.1.dist-info/RECORD +0 -155
- {vgit → visidata/apps/vgit}/__main__.py +0 -0
- {vgit → visidata/apps/vgit}/abort.py +0 -0
- /visidata/{repeat.py → features/repeat.py} +0 -0
- {visidata-2.11.1.data → visidata-3.0.1.data}/scripts/vd +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/entry_points.txt +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,161 @@
|
|
1
|
+
import io
|
2
|
+
|
3
|
+
from visidata import AttrDict, vd, Path, asyncthread, Sheet
|
4
|
+
|
5
|
+
|
6
|
+
class GitSheet(Sheet):
|
7
|
+
@property
|
8
|
+
def gitargstr(self):
|
9
|
+
return ' '.join(self.gitargs)
|
10
|
+
|
11
|
+
def git(self, subcmd, *args, **kwargs):
|
12
|
+
'For non-modifying commands; not logged except in debug mode'
|
13
|
+
sh = vd.importExternal('sh')
|
14
|
+
args = list(subcmd.split()) + list(args)
|
15
|
+
vd.debug('git ' + ' '.join(str(x) for x in args))
|
16
|
+
return sh.git(*args,
|
17
|
+
_cwd=self.gitRootPath,
|
18
|
+
**kwargs)
|
19
|
+
|
20
|
+
def loggit(self, subcmd, *args, **kwargs):
|
21
|
+
'Run git command with *args*, and post a status message.'
|
22
|
+
import sh
|
23
|
+
args = list(subcmd.split()) + list(args)
|
24
|
+
vd.warning('git ' + ' '.join(str(x) for x in args))
|
25
|
+
return sh.git(*args,
|
26
|
+
_cwd=self.gitRootPath,
|
27
|
+
**kwargs)
|
28
|
+
|
29
|
+
def git_all(self, *args, **kwargs):
|
30
|
+
'Return entire output of git command.'
|
31
|
+
sh = vd.importExternal('sh')
|
32
|
+
try:
|
33
|
+
vd.debug('git ' + ' '.join(str(x) for x in args))
|
34
|
+
out = self.git('--no-pager',
|
35
|
+
*args,
|
36
|
+
_decode_errors='replace',
|
37
|
+
_bg_exc=False,
|
38
|
+
**kwargs)
|
39
|
+
except sh.ErrorReturnCode as e:
|
40
|
+
vd.warning('git '+' '.join(str(x) for x in args), 'error=%s' % e.exit_code)
|
41
|
+
out = e.stdout
|
42
|
+
|
43
|
+
return out
|
44
|
+
|
45
|
+
def git_lines(self, subcmd, *args, **kwargs):
|
46
|
+
'Generator of stdout lines from given git command'
|
47
|
+
sh = vd.importExternal('sh')
|
48
|
+
err = io.StringIO()
|
49
|
+
args = list(subcmd.split()) + list(args)
|
50
|
+
try:
|
51
|
+
vd.debug('git ' + ' '.join(str(x) for x in args))
|
52
|
+
for line in self.git('--no-pager',
|
53
|
+
*args,
|
54
|
+
_decode_errors='replace',
|
55
|
+
_iter=True,
|
56
|
+
_bg_exc=False,
|
57
|
+
_err=err,
|
58
|
+
**kwargs):
|
59
|
+
yield line[:-1] # remove EOL
|
60
|
+
|
61
|
+
except sh.ErrorReturnCode as e:
|
62
|
+
vd.warning('git '+' '.join(str(x) for x in args), 'error=%s' % e.exit_code)
|
63
|
+
|
64
|
+
errlines = err.getvalue().splitlines()
|
65
|
+
if errlines:
|
66
|
+
vd.warning('git stderr: ' + '\n'.join(errlines))
|
67
|
+
|
68
|
+
|
69
|
+
def git_iter(self, subcmd, *args, sep='\0', **kwargs):
|
70
|
+
'Generator of chunks of stdout from given git command *subcmd*, delineated by sep character.'
|
71
|
+
sh = vd.importExternal('sh')
|
72
|
+
import sh
|
73
|
+
err = io.StringIO()
|
74
|
+
|
75
|
+
args = list(subcmd.split()) + list(args)
|
76
|
+
bufsize = 512
|
77
|
+
chunks = []
|
78
|
+
try:
|
79
|
+
vd.debug('git ' + ' '.join(str(x) for x in args))
|
80
|
+
for data in self.git('--no-pager',
|
81
|
+
*args,
|
82
|
+
_decode_errors='replace',
|
83
|
+
_out_bufsize=bufsize,
|
84
|
+
_iter=True,
|
85
|
+
_bg_exc=False,
|
86
|
+
_err=err,
|
87
|
+
**kwargs):
|
88
|
+
while True:
|
89
|
+
i = data.find(sep)
|
90
|
+
if i < 0:
|
91
|
+
break
|
92
|
+
chunks.append(data[:i])
|
93
|
+
data = data[i+1:]
|
94
|
+
yield ''.join(chunks)
|
95
|
+
chunks.clear()
|
96
|
+
|
97
|
+
chunks.append(data)
|
98
|
+
except sh.ErrorReturnCode as e:
|
99
|
+
vd.warning('git '+' '.join(str(x) for x in args), 'error=%s' % e.exit_code)
|
100
|
+
|
101
|
+
if chunks:
|
102
|
+
yield ''.join(chunks)
|
103
|
+
|
104
|
+
errlines = err.getvalue().splitlines()
|
105
|
+
if errlines:
|
106
|
+
vd.warning('git stderr: ' + '\n'.join(errlines))
|
107
|
+
|
108
|
+
@asyncthread
|
109
|
+
def modifyGit(self, *args, **kwargs):
|
110
|
+
'Run git command that modifies the repo'
|
111
|
+
vd.warning('git ' + ' '.join(str(x) for x in args))
|
112
|
+
ret = self.git_all(*args, **kwargs)
|
113
|
+
vd.status(ret)
|
114
|
+
|
115
|
+
if isinstance(self.source, GitSheet):
|
116
|
+
self.source.reload()
|
117
|
+
|
118
|
+
self.reload()
|
119
|
+
|
120
|
+
@property
|
121
|
+
def gitRootSheet(self):
|
122
|
+
if isinstance(self.source, GitSheet):
|
123
|
+
return self.source.gitRootSheet
|
124
|
+
return self
|
125
|
+
|
126
|
+
def iterload(self):
|
127
|
+
for line in self.git_lines(*self.gitargs):
|
128
|
+
yield AttrDict(line=line)
|
129
|
+
|
130
|
+
|
131
|
+
@GitSheet.lazy_property
|
132
|
+
def gitRootPath(self):
|
133
|
+
'Return Path of git root (nearest ancestor directory with a .git/)'
|
134
|
+
def _getRepoPath(p):
|
135
|
+
'Return path at p or above which has .git subdir'
|
136
|
+
if p.joinpath('.git').exists():
|
137
|
+
return p
|
138
|
+
if str(p) in ['/','']:
|
139
|
+
return None
|
140
|
+
return _getRepoPath(p.resolve().parent)
|
141
|
+
|
142
|
+
p = _getRepoPath(self.gitRootSheet.source)
|
143
|
+
if p:
|
144
|
+
return p
|
145
|
+
|
146
|
+
|
147
|
+
@GitSheet.lazy_property
|
148
|
+
def branch(self):
|
149
|
+
return self.git('rev-parse', '--abbrev-ref', 'HEAD').strip()
|
150
|
+
|
151
|
+
|
152
|
+
GitSheet.options.disp_note_none = ''
|
153
|
+
GitSheet.options.disp_status_fmt = '{sheet.progressStatus}‹{sheet.branchStatus}› {sheet.name}| '
|
154
|
+
|
155
|
+
GitSheet.addCommand('gi', 'git-exec', 'cmdstr=input("gi", type="git"); vd.push(GitSheet(cmdstr, gitargs=cmdstr.split()))', 'execute git command')
|
156
|
+
|
157
|
+
GitSheet.addCommand('Alt+g', 'menu-git', 'pressMenu("Git")', '')
|
158
|
+
|
159
|
+
vd.addMenuItems('''
|
160
|
+
Git > Execute command > git-exec
|
161
|
+
''')
|
@@ -4,13 +4,13 @@ from .gitsheet import GitSheet
|
|
4
4
|
|
5
5
|
|
6
6
|
@VisiData.api
|
7
|
-
def git_grep(vd, args):
|
8
|
-
return GitGrep(args[0], regex=args[0], source=
|
7
|
+
def git_grep(vd, p, args):
|
8
|
+
return GitGrep(args[0], regex=args[0], source=p)
|
9
9
|
|
10
10
|
|
11
11
|
class GitGrep(GitSheet):
|
12
12
|
rowtype = 'results' # rowdef: list(file, line, line_contents)
|
13
|
-
|
13
|
+
guide = '''
|
14
14
|
# vgit grep
|
15
15
|
Each row on this sheet is a line matching the regex pattern `{sheet.regex}` in the tracked files of the current directory.
|
16
16
|
|
@@ -27,10 +27,11 @@ class GitGrep(GitSheet):
|
|
27
27
|
tmp = (self.topRowIndex, self.cursorRowIndex)
|
28
28
|
for line in self.git_lines('grep', '--no-color', '-z', '--line-number', '--ignore-case', self.regex):
|
29
29
|
# line = line.replace(ESC+'[1;31m', '[:green]')
|
30
|
-
# line = line.replace(ESC+'[m', '[
|
30
|
+
# line = line.replace(ESC+'[m', '[/]')
|
31
31
|
yield list(line.split('\0'))
|
32
32
|
self.topRowIndex, self.cursorRowIndex = tmp
|
33
33
|
|
34
34
|
|
35
|
-
GitSheet.addCommand('g/', 'git-grep', 'rex=
|
35
|
+
GitSheet.addCommand('g/', 'git-grep', 'rex=inputRegex("git grep: "); vd.push(GitGrep(rex, regex=rex, source=sheet))', 'find in all files in this repo')
|
36
36
|
GitGrep.addCommand('Ctrl+O', 'sysopen-row', 'launchExternalEditorPath(Path(cursorRow[0]), linenum=cursorRow[1]); reload()', 'open this file in $EDITOR')
|
37
|
+
GitGrep.bindkey('Enter', 'sysopen-row')
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import functools
|
2
|
+
|
3
|
+
from visidata import vd, VisiData, Column, ItemColumn, date, RowColorizer, asyncthread, Progress, AttrDict
|
4
|
+
|
5
|
+
from .gitsheet import GitSheet
|
6
|
+
|
7
|
+
|
8
|
+
@VisiData.api
|
9
|
+
def git_log(vd, p, *args):
|
10
|
+
return GitLogSheet('git-log', source=p, gitargs=args)
|
11
|
+
|
12
|
+
# rowdef: (commit_hash, refnames, author, author_date, body, notes)
|
13
|
+
class GitLogSheet(GitSheet):
|
14
|
+
guide = '''
|
15
|
+
# git log {sheet.gitargstr}
|
16
|
+
{sheet.cursorRow.message}
|
17
|
+
'''
|
18
|
+
GIT_LOG_FORMAT = ['%H', '%D', '%an <%ae>', '%ai', '%B', '%N']
|
19
|
+
rowtype = 'commits' # rowdef: AttrDict
|
20
|
+
defer = True
|
21
|
+
savesToSource = True
|
22
|
+
columns = [
|
23
|
+
ItemColumn('commitid', width=8),
|
24
|
+
ItemColumn('refnames', width=12),
|
25
|
+
ItemColumn('message', type=str.strip, setter=lambda c,r,v: c.sheet.git('commit --amend --no-edit --quiet --message', v), width=50),
|
26
|
+
ItemColumn('author', setter=lambda c,r,v: c.sheet.git('commit --amend --no-edit --quiet --author', v)),
|
27
|
+
ItemColumn('author_date', type=date, setter=lambda c,r,v: c.sheet.git('commit --amend --no-edit --quiet --date', v)),
|
28
|
+
ItemColumn('notes', setter=lambda c,r,v: c.sheet.git('notes add --force --message', v, r.commitid)),
|
29
|
+
]
|
30
|
+
colorizers = [
|
31
|
+
RowColorizer(5, 'color_vgit_unpushed', lambda s,c,r,v: r and not s.inRemoteBranch(r.commitid)),
|
32
|
+
]
|
33
|
+
|
34
|
+
def __init__(self, *args, **kwargs):
|
35
|
+
super().__init__(*args, **kwargs)
|
36
|
+
|
37
|
+
@functools.lru_cache()
|
38
|
+
def inRemoteBranch(self, commitid):
|
39
|
+
return self.git_all('branch -r --contains', commitid, _ok_code=[0, 1])
|
40
|
+
|
41
|
+
def iterload(self):
|
42
|
+
lines = self.git_iter('log --no-color -z', '--pretty=format:' + '%x1f'.join(self.GIT_LOG_FORMAT), *self.gitargs)
|
43
|
+
for record in Progress(tuple(lines)):
|
44
|
+
r = record.split('\x1f')
|
45
|
+
yield AttrDict(
|
46
|
+
commitid=r[0],
|
47
|
+
refnames=r[1],
|
48
|
+
author=r[2],
|
49
|
+
author_date=r[3],
|
50
|
+
message=r[4],
|
51
|
+
notes=r[5],
|
52
|
+
)
|
53
|
+
|
54
|
+
def openRow(self, row):
|
55
|
+
'open this commit'
|
56
|
+
return getCommitSheet(row[0][:7], self, row[0])
|
57
|
+
|
58
|
+
@asyncthread
|
59
|
+
def commit(self, path, adds, mods, dels):
|
60
|
+
|
61
|
+
assert not adds
|
62
|
+
assert not dels
|
63
|
+
|
64
|
+
for row, rowmods in mods.values():
|
65
|
+
for col, val in rowmods.values():
|
66
|
+
vd.callNoExceptions(col.putValue, row, val)
|
67
|
+
|
68
|
+
self.reload()
|
69
|
+
self.resetDeferredCommit()
|
70
|
+
|
71
|
+
|
72
|
+
GitLogSheet.addCommand(None, 'delete-row', 'error("delete is not supported")')
|
73
|
+
GitLogSheet.addCommand(None, 'add-row', 'error("commits cannot be added")')
|
74
|
+
#GitLogSheet.addCommand('x', 'git-pick', 'git("cherry-pick", cursorRow.commitid)', 'cherry-pick this commit onto current branch')
|
75
|
+
#GitLogSheet.addCommand('r', 'git-reset-here', 'git("update-ref", "refs/heads/"+source, cursorRow[0])', 'reset this branch to this commit')
|
76
|
+
|
77
|
+
GitSheet.addCommand('', 'git-log', 'vd.push(git_log(gitRootPath, branch))', 'push log of current branch')
|
78
|
+
|
79
|
+
vd.addMenuItems('''
|
80
|
+
Git > Open > log > git-log
|
81
|
+
''')
|
@@ -4,9 +4,10 @@
|
|
4
4
|
The syntax for vgit is the same as the syntax for git.
|
5
5
|
By default, will pass the command to git verbatim, as quickly as possible.
|
6
6
|
If vgit can provide an interactive interface for a particular subcommand,
|
7
|
-
it will open the sheet returned by vd.git_<subcommand>().
|
7
|
+
it will open the sheet returned by vd.git_<subcommand>(path, args).
|
8
8
|
'''
|
9
9
|
|
10
|
+
import os
|
10
11
|
import sys
|
11
12
|
|
12
13
|
|
@@ -20,23 +21,35 @@ def vgit_cli():
|
|
20
21
|
args.remove('--debug')
|
21
22
|
|
22
23
|
if not args:
|
23
|
-
|
24
|
-
return
|
24
|
+
args = ['help']
|
25
25
|
|
26
26
|
func = getattr(vd, 'git_'+args[0], None)
|
27
27
|
if func:
|
28
28
|
vd.loadConfigAndPlugins()
|
29
29
|
vd.status(visidata.__version_info__)
|
30
30
|
vd.domotd()
|
31
|
+
if flDebug:
|
32
|
+
vd.options.debug = True
|
31
33
|
|
34
|
+
rc = 0
|
32
35
|
try:
|
33
|
-
|
36
|
+
p = Path('.')
|
37
|
+
vs = func(p, args[1:])
|
34
38
|
if vs:
|
35
|
-
|
39
|
+
vd.run(vs)
|
40
|
+
except BrokenPipeError:
|
41
|
+
os.dup2(os.open(os.devnull, os.O_WRONLY), sys.stdout.fileno()) # handle broken pipe gracefully
|
42
|
+
except visidata.ExpectedException as e:
|
43
|
+
print(str(e))
|
36
44
|
except Exception as e:
|
45
|
+
rc = 1
|
37
46
|
vd.exceptionCaught(e)
|
38
47
|
if flDebug:
|
39
48
|
raise
|
40
49
|
|
50
|
+
sys.stderr.flush()
|
51
|
+
sys.stdout.flush()
|
52
|
+
os._exit(rc) # cleanup can be expensive
|
53
|
+
|
41
54
|
import subprocess
|
42
55
|
return subprocess.run(['git', *args]).returncode
|
@@ -3,13 +3,13 @@ from visidata import vd, VisiData, ItemColumn, AttrDict, RowColorizer, Path
|
|
3
3
|
from .gitsheet import GitSheet
|
4
4
|
|
5
5
|
@VisiData.api
|
6
|
-
def git_remote(vd, args
|
6
|
+
def git_remote(vd, p, args):
|
7
7
|
if not args or 'show' in args:
|
8
|
-
return GitRemotes('remotes', source=
|
8
|
+
return GitRemotes('remotes', source=p)
|
9
9
|
|
10
10
|
|
11
11
|
class GitRemotes(GitSheet):
|
12
|
-
|
12
|
+
guide = '''
|
13
13
|
# git remote
|
14
14
|
Manage the set of repositories ("remotes") whose branches you track.
|
15
15
|
|
@@ -50,4 +50,8 @@ class GitRemotes(GitSheet):
|
|
50
50
|
return AttrDict()
|
51
51
|
|
52
52
|
|
53
|
-
|
53
|
+
GitSheet.addCommand('', 'git-open-remotes', 'vd.push(git_remote(Path("."), ""))', 'open git remotes sheet')
|
54
|
+
|
55
|
+
vd.addMenuItems('''
|
56
|
+
Git > Open > remotes > git-open-remotes
|
57
|
+
''')
|
@@ -0,0 +1,71 @@
|
|
1
|
+
from visidata import vd, VisiData, Sheet, Column, AttrColumn, date, vlen, asyncthread, Path, namedlist, PyobjSheet, modtime, AttrDict
|
2
|
+
|
3
|
+
from .gitsheet import GitSheet
|
4
|
+
|
5
|
+
@VisiData.api
|
6
|
+
def guess_git(vd, p):
|
7
|
+
if (p/'.git').is_dir():
|
8
|
+
return dict(filetype='git', _likelihood=10)
|
9
|
+
|
10
|
+
|
11
|
+
@VisiData.api
|
12
|
+
def open_git(vd, p):
|
13
|
+
return vd.git_status(p, [])
|
14
|
+
|
15
|
+
|
16
|
+
@VisiData.api
|
17
|
+
def git_repos(vd, p, args):
|
18
|
+
return GitRepos(p.base_stem, source=p)
|
19
|
+
|
20
|
+
|
21
|
+
class GitLinesColumn(Column):
|
22
|
+
def __init__(self, name, cmd, *args, **kwargs):
|
23
|
+
super().__init__(name, cache='async', **kwargs)
|
24
|
+
cmdparts = cmd.split()
|
25
|
+
if cmdparts[0] == 'git':
|
26
|
+
cmdparts = cmdparts[1:]
|
27
|
+
self.gitargs = cmdparts + list(args)
|
28
|
+
|
29
|
+
def calcValue(self, r):
|
30
|
+
lines = list(GitSheet(source=r).git_lines(*self.gitargs))
|
31
|
+
if lines:
|
32
|
+
return lines
|
33
|
+
|
34
|
+
|
35
|
+
class GitAllColumn(GitLinesColumn):
|
36
|
+
def calcValue(self, r):
|
37
|
+
return GitSheet(source=r).git_all(*self.gitargs).strip()
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
class GitRepos(GitSheet):
|
42
|
+
guide = '''
|
43
|
+
# git repos
|
44
|
+
A list of git repositories under `{sheet.source}`
|
45
|
+
|
46
|
+
- `Enter` to open the status sheet for the current repo
|
47
|
+
'''
|
48
|
+
rowtype = 'git repos' # rowdef: Path
|
49
|
+
columns = [
|
50
|
+
Column('repo', type=str, width=30),
|
51
|
+
GitAllColumn('branch', 'git rev-parse --abbrev-ref HEAD', width=8),
|
52
|
+
GitLinesColumn('diffs', 'git diff --no-color', type=vlen, width=8),
|
53
|
+
GitLinesColumn('staged_diffs', 'git diff --cached', type=vlen, width=8),
|
54
|
+
GitLinesColumn('branches', 'git branch --no-color', type=vlen, width=10),
|
55
|
+
GitLinesColumn('stashes', 'git stash list', type=vlen, width=8),
|
56
|
+
Column('modtime', type=date, getter=lambda c,r: modtime(r)),
|
57
|
+
]
|
58
|
+
nKeys = 1
|
59
|
+
|
60
|
+
def iterload(self):
|
61
|
+
import glob
|
62
|
+
for fn in glob.glob('**/.git', root_dir=self.source, recursive=True):
|
63
|
+
yield Path(fn).parent
|
64
|
+
|
65
|
+
|
66
|
+
def openRow(self, row):
|
67
|
+
return vd.git_status(row, [])
|
68
|
+
|
69
|
+
def openCell(self, col, row):
|
70
|
+
val = col.getValue(row)
|
71
|
+
return PyobjSheet(getattr(val, '__name__', ''), source=val)
|
@@ -2,19 +2,21 @@
|
|
2
2
|
|
3
3
|
from setuptools import setup, find_packages
|
4
4
|
|
5
|
+
# Note: use `python3 visidata/apps/setup.py install` from the root directory
|
6
|
+
|
5
7
|
__version__ = '0.2-dev'
|
6
8
|
|
7
9
|
setup(name='vgit',
|
8
10
|
version=__version__,
|
9
11
|
description='a sleek terminal user interface for git',
|
10
12
|
# long_description=open('README.md').read(),
|
11
|
-
install_requires=['sh'], # visidata
|
12
|
-
|
13
|
-
|
13
|
+
install_requires=['sh<2'], # visidata
|
14
|
+
packages=find_packages(exclude=["tests"]),
|
15
|
+
scripts=['vgit'],
|
16
|
+
entry_points={'visidata.plugins': 'vgit=visidata.apps.vgit'},
|
14
17
|
author='Saul Pwanson',
|
15
18
|
author_email='vgit@saul.pw',
|
16
19
|
url='https://github.com/saulpw/visidata/vgit',
|
17
|
-
scripts=['bin/vgit'],
|
18
20
|
license='GPLv3',
|
19
21
|
python_requires='>=3.7',
|
20
22
|
classifiers=[
|
@@ -0,0 +1,69 @@
|
|
1
|
+
from visidata import vd, VisiData, ItemColumn, AttrDict
|
2
|
+
|
3
|
+
|
4
|
+
from .gitsheet import GitSheet
|
5
|
+
from .diff import GitDiffSheet
|
6
|
+
|
7
|
+
|
8
|
+
@VisiData.api
|
9
|
+
def git_stash(vd, p, args):
|
10
|
+
if 'list' in args:
|
11
|
+
return GitStashes('git-stash-list', source=p, gitargs=args)
|
12
|
+
|
13
|
+
|
14
|
+
class GitStashes(GitSheet):
|
15
|
+
guide = '''
|
16
|
+
# git stash
|
17
|
+
This is the list of changes that have been stashed previously.
|
18
|
+
|
19
|
+
`a` to apply this stashed change (without removing it)
|
20
|
+
`d` to drop this stashed change
|
21
|
+
`b` to create a branch from this stashed change'),
|
22
|
+
'''
|
23
|
+
rowtype = 'stashed change' # rowdef: AttrDict(stashid=, branched_from=, sha1=, msg=)
|
24
|
+
columns = [
|
25
|
+
ItemColumn('stashid'),
|
26
|
+
ItemColumn('branched_from'),
|
27
|
+
ItemColumn('sha1'),
|
28
|
+
ItemColumn('msg'),
|
29
|
+
ItemColumn('line', width=0),
|
30
|
+
]
|
31
|
+
|
32
|
+
def iterload(self):
|
33
|
+
for line in self.git_lines('stash', *self.gitargs):
|
34
|
+
stashid, ctx, rest = line.split(': ', 2)
|
35
|
+
if ctx.startswith('WIP on '):
|
36
|
+
branched_from = ctx[len('WIP on '):]
|
37
|
+
sha1, msg = rest.split(' ', 1)
|
38
|
+
elif ctx.startswith('On '):
|
39
|
+
branched_from = ctx[len('On '):]
|
40
|
+
sha1 = ''
|
41
|
+
msg = rest
|
42
|
+
yield AttrDict(
|
43
|
+
line=line,
|
44
|
+
stashid=stashid,
|
45
|
+
branched_from=branched_from,
|
46
|
+
sha1=sha1,
|
47
|
+
msg=msg.strip(),
|
48
|
+
)
|
49
|
+
|
50
|
+
def openRow(self, row):
|
51
|
+
'open this stashed change'
|
52
|
+
return GitDiffSheet(row.stashid, "diffs", gitargs=['stash show --no-color --patch', row.stashid], source=self.source)
|
53
|
+
|
54
|
+
|
55
|
+
GitSheet.addCommand('', 'git-open-stashes', 'vd.push(git_stash(source, ["list"]))', 'push stashes sheet')
|
56
|
+
|
57
|
+
GitStashes.addCommand('a', 'git-stash-apply', 'loggit("stash", "apply", cursorRow[0])', 'apply this stashed change without removing')
|
58
|
+
GitStashes.addCommand('', 'git-stash-pop', 'loggit("stash", "pop", cursorRow[0])', 'apply this stashed change and drop it')
|
59
|
+
GitStashes.addCommand('d', 'git-stash-drop', 'loggit("stash", "drop", cursorRow[0])', 'drop this stashed change')
|
60
|
+
GitStashes.addCommand('b', 'git-stash-branch', 'loggit("stash", "branch", input("create branch from stash named: "), cursorRow[0])', 'create branch from stash')
|
61
|
+
|
62
|
+
|
63
|
+
vd.addMenuItems('''
|
64
|
+
Git > Open > stashes > git-open-stashes
|
65
|
+
Git > Stash > apply > git-stash-apply
|
66
|
+
Git > Stash > drop > git-stash-drop
|
67
|
+
Git > Stash > apply then drop > git-stash-pop
|
68
|
+
Git > Stash > create branch > git-stash-branch
|
69
|
+
''')
|