Skip to content

Commit 123ac24

Browse files
committed
Merge branch 'develop'
2 parents f5637d7 + eaa55e6 commit 123ac24

File tree

10 files changed

+388
-56
lines changed

10 files changed

+388
-56
lines changed

gitless/cli/gl.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from gitless import core
1818

1919
from . import (
20-
gl_track, gl_untrack, gl_status, gl_diff, gl_commit, gl_branch,
20+
gl_track, gl_untrack, gl_status, gl_diff, gl_commit, gl_branch, gl_tag,
2121
gl_checkout, gl_merge, gl_resolve, gl_fuse, gl_remote, gl_publish,
2222
gl_switch, gl_init, gl_history)
2323
from . import pprint
@@ -29,7 +29,7 @@
2929
INTERNAL_ERROR = 3
3030
NOT_IN_GL_REPO = 4
3131

32-
VERSION = '0.8.2'
32+
VERSION = '0.8.3'
3333
URL = 'http://gitless.com'
3434

3535

@@ -55,7 +55,7 @@ def main():
5555
subparsers.required = True
5656

5757
sub_cmds = [
58-
gl_track, gl_untrack, gl_status, gl_diff, gl_commit, gl_branch,
58+
gl_track, gl_untrack, gl_status, gl_diff, gl_commit, gl_branch, gl_tag,
5959
gl_checkout, gl_merge, gl_resolve, gl_fuse, gl_remote, gl_publish,
6060
gl_switch, gl_init, gl_history]
6161
for sub_cmd in sub_cmds:

gitless/cli/gl_branch.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@
77

88
from __future__ import unicode_literals
99

10-
try:
11-
from StringIO import StringIO
12-
except ImportError:
13-
from io import StringIO
14-
1510
from clint.textui import colored
1611

1712
from gitless import core
@@ -93,14 +88,14 @@ def _do_list(repo, list_remote, v=False):
9388
pprint.item(
9489
'{0} {1} {2}'.format(current_str, color(b.branch_name), upstream_str))
9590
if v:
96-
pprint.item(' ➜ head is {0}'.format(_ci_str(b.head)))
91+
pprint.item(' ➜ head is {0}'.format(pprint.commit_str(b.head)))
9792

9893
if list_remote:
9994
for r in repo.remotes:
10095
for b in (r.lookup_branch(n) for n in r.listall_branches()):
10196
pprint.item(' {0}'.format(colored.yellow(str(b))))
10297
if v:
103-
pprint.item(' ➜ head is {0}'.format(_ci_str(b.head)))
98+
pprint.item(' ➜ head is {0}'.format(pprint.commit_str(b.head)))
10499

105100

106101
def _do_create(create_b, dp, repo):
@@ -190,11 +185,5 @@ def _do_set_head(commit_id, repo):
190185
curr_b = repo.current_branch
191186
curr_b.head = commit.id
192187
pprint.ok(
193-
'Head of current branch {0} is now {1}'.format(curr_b, _ci_str(commit)))
188+
'Head of current branch {0} is now {1}'.format(curr_b, pprint.commit_str(commit)))
194189
return True
195-
196-
197-
def _ci_str(ci):
198-
ci_str = StringIO()
199-
pprint.commit(ci, compact=True, stream=ci_str.write)
200-
return ci_str.getvalue().strip()

gitless/cli/gl_commit.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
from __future__ import unicode_literals
99

10+
import subprocess
11+
from sh import git
12+
1013
from gitless import core
1114

1215
from . import commit_dialog
@@ -24,6 +27,10 @@ def parser(subparsers, repo):
2427
'flags'))
2528
commit_parser.add_argument(
2629
'-m', '--message', help='Commit message', dest='m')
30+
commit_parser.add_argument(
31+
'-p', '--partial',
32+
help='Interactively select segments of files to commit', dest='p',
33+
action='store_true')
2734
helpers.oei_flags(commit_parser, repo)
2835
commit_parser.set_defaults(func=main)
2936

@@ -36,13 +43,19 @@ def main(args, repo):
3643
pprint.err_exp('use gl track f if you want to track changes to file f')
3744
return False
3845

46+
curr_b = repo.current_branch
47+
partials = None
48+
if args.p:
49+
partials = _do_partial_selection(commit_files, curr_b)
50+
3951
msg = args.m if args.m else commit_dialog.show(commit_files, repo)
4052
if not msg.strip():
53+
if partials:
54+
git.reset('HEAD', partials)
4155
raise ValueError('Missing commit message')
4256

43-
curr_b = repo.current_branch
4457
_auto_track(commit_files, curr_b)
45-
ci = curr_b.create_commit(commit_files, msg)
58+
ci = curr_b.create_commit(commit_files, msg, partials=partials)
4659
pprint.ok('Commit succeeded')
4760

4861
pprint.blank()
@@ -56,6 +69,24 @@ def main(args, repo):
5669
return True
5770

5871

72+
def _do_partial_selection(files, curr_b):
73+
partials = []
74+
for fp in files:
75+
f_st = curr_b.status_file(fp)
76+
if not f_st.exists_at_head:
77+
pprint.warn('Can\'t select segments for new file {0}'.format(fp))
78+
continue
79+
if not f_st.exists_in_wd:
80+
pprint.warn('Can\'t select segments for deleted file {0}'.format(fp))
81+
continue
82+
83+
subprocess.call(['git', 'add', '-p', fp])
84+
# TODO: check that at least one hunk was staged
85+
partials.append(fp)
86+
87+
return partials
88+
89+
5990
def _auto_track(files, curr_b):
6091
"""Tracks those untracked files in the list."""
6192
for fp in files:

gitless/cli/gl_fuse.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def parser(subparsers, repo):
4040
fuse_parser.add_argument(
4141
'-ip', '--insertion-point', nargs='?',
4242
help=(
43-
'the commit where to put the divergent changes, dp for '
43+
'the divergent changes will be inserted after the commit given, dp for '
4444
'divergent point is the default'), metavar='commit_id')
4545
fuse_parser.add_argument(
4646
'-a', '--abort', help='abort the fuse in progress', action='store_true')
@@ -55,11 +55,21 @@ def main(args, repo):
5555
return True
5656

5757
src_branch = helpers.get_branch_or_use_upstream(args.src, 'src', repo)
58-
dp = repo.merge_base(current_b, src_branch)
58+
59+
mb = repo.merge_base(current_b, src_branch)
60+
if mb == src_branch.target: # the current branch is ahead or both branches are equal
61+
pprint.err('No commits to fuse')
62+
return False
63+
64+
if (not args.insertion_point or args.insertion_point == 'dp' or
65+
args.insertion_point == 'divergent-point'):
66+
insertion_point = mb
67+
else:
68+
insertion_point = repo.revparse_single(args.insertion_point).id
5969

6070
def valid_input(inp):
6171
walker = src_branch.history()
62-
walker.hide(dp)
72+
walker.hide(insertion_point)
6373
divergent_ids = frozenset(ci.id for ci in walker)
6474

6575
errors_found = False
@@ -81,11 +91,6 @@ def valid_input(inp):
8191
if not valid_input(exclude):
8292
return False
8393

84-
if (not args.insertion_point or args.insertion_point == 'dp' or
85-
args.insertion_point == 'divergent-point'):
86-
insertion_point = dp
87-
else:
88-
insertion_point = repo.revparse_single(args.insertion_point).id
8994

9095
try:
9196
current_b.fuse(

gitless/cli/gl_tag.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# -*- coding: utf-8 -*-
2+
# Gitless - a version control system built on top of Git.
3+
# Licensed under GNU GPL v2.
4+
5+
"""gl tag - List, create, edit or delete tags."""
6+
7+
8+
from __future__ import unicode_literals
9+
10+
from gitless import core
11+
12+
from . import helpers, pprint
13+
14+
15+
def parser(subparsers, _):
16+
"""Adds the tag parser to the given subparsers object."""
17+
desc = 'list, create, or delete tags'
18+
tag_parser = subparsers.add_parser(
19+
'tag', help=desc, description=desc.capitalize())
20+
tag_parser.add_argument(
21+
'-r', '--remote',
22+
help='list remote tags in addition to local tags',
23+
action='store_true')
24+
25+
tag_parser.add_argument(
26+
'-c', '--create', nargs='+', help='create tag(s)', dest='create_t',
27+
metavar='tag')
28+
tag_parser.add_argument(
29+
'-ci', '--commit',
30+
help='the commit to tag (only relevant if a new '
31+
'tag is created; defaults to the HEAD commit)', default='HEAD',
32+
dest='ci')
33+
tag_parser.add_argument(
34+
'-d', '--delete', nargs='+', help='delete tag(s)', dest='delete_t',
35+
metavar='tag')
36+
37+
tag_parser.set_defaults(func=main)
38+
39+
40+
def main(args, repo):
41+
ret = True
42+
if args.create_t:
43+
ret = _do_create(args.create_t, args.ci, repo)
44+
elif args.delete_t:
45+
ret = _do_delete(args.delete_t, repo)
46+
else:
47+
_do_list(repo, args.remote)
48+
49+
return ret
50+
51+
52+
def _do_list(repo, list_remote):
53+
pprint.msg('List of tags:')
54+
pprint.exp('do gl tag -c t to create tag t')
55+
pprint.exp('do gl tag -d t to delete tag t')
56+
pprint.blank()
57+
58+
no_tags = True
59+
for t in (repo.lookup_tag(n) for n in repo.listall_tags()):
60+
pprint.item('{0} ➜ tags {1}'.format(t, pprint.commit_str(t.commit)))
61+
no_tags = False
62+
63+
if list_remote:
64+
for r in repo.remotes:
65+
for t in (r.lookup_tag(n) for n in r.listall_tags()):
66+
pprint.item('{0} ➜ tags {1}'.format(t, pprint.commit_str(t.commit)))
67+
no_tags = False
68+
69+
if no_tags:
70+
pprint.item('There are no tags to list')
71+
72+
73+
def _do_create(create_t, dp, repo):
74+
errors_found = False
75+
76+
try:
77+
target = repo.revparse_single(dp)
78+
except KeyError:
79+
raise ValueError('Invalid commit {0}'.format(dp))
80+
81+
for t_name in create_t:
82+
r = repo
83+
remote_str = ''
84+
if '/' in t_name: # might want to create a remote tag
85+
maybe_remote, maybe_remote_tag = t_name.split('/', 1)
86+
if maybe_remote in repo.remotes:
87+
r = repo.remotes[maybe_remote]
88+
t_name = maybe_remote_tag
89+
conf_msg = 'Tag {0} will be created in remote repository {1}'.format(
90+
t_name, maybe_remote)
91+
if not pprint.conf_dialog(conf_msg):
92+
pprint.msg(
93+
'Aborted: creation of tag {0} in remote repository {1}'.format(
94+
t_name, maybe_remote))
95+
continue
96+
remote_str = ' in remote repository {0}'.format(maybe_remote)
97+
try:
98+
r.create_tag(t_name, target)
99+
pprint.ok('Created new tag {0}{1}'.format(t_name, remote_str))
100+
except ValueError as e:
101+
pprint.err(e)
102+
errors_found = True
103+
104+
return not errors_found
105+
106+
107+
def _do_delete(delete_t, repo):
108+
errors_found = False
109+
110+
for t_name in delete_t:
111+
try:
112+
t = helpers.get_tag(t_name, repo)
113+
114+
tag_str = 'Tag {0} will be removed'.format(t.tag_name)
115+
remote_str = ''
116+
if isinstance(t, core.RemoteTag):
117+
remote_str = 'from remote repository {0}'.format(t.remote_name)
118+
if not pprint.conf_dialog('{0} {1}'.format(tag_str, remote_str)):
119+
pprint.msg('Aborted: removal of tag {0}'.format(t))
120+
continue
121+
122+
t.delete()
123+
pprint.ok('Tag {0} removed successfully'.format(t))
124+
except ValueError as e:
125+
pprint.err(e)
126+
errors_found = True
127+
128+
return not errors_found

gitless/cli/helpers.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,35 @@
1818

1919

2020
def get_branch(branch_name, repo):
21-
b = repo.lookup_branch(branch_name)
22-
if not b:
23-
if '/' not in branch_name:
24-
raise ValueError('Branch "{0}" doesn\'t exist'.format(branch_name))
21+
return _get_ref("branch", branch_name, repo)
2522

26-
# It might be a remote branch
27-
remote, remote_branch = branch_name.split('/', 1)
23+
24+
def get_tag(tag_name, repo):
25+
return _get_ref("tag", tag_name, repo)
26+
27+
28+
def _get_ref(ref_type, ref_name, repo):
29+
ref_type_cap = ref_type.capitalize()
30+
r = getattr(repo, "lookup_" + ref_type)(ref_name)
31+
if not r:
32+
if '/' not in ref_name:
33+
raise ValueError(
34+
'{0} "{1}" doesn\'t exist'.format(ref_type_cap, ref_name))
35+
36+
# It might be a remote ref
37+
remote, remote_ref = ref_name.split('/', 1)
2838
try:
29-
r = repo.remotes[remote]
39+
remote_repo = repo.remotes[remote]
3040
except KeyError:
3141
raise ValueError(
3242
'Remote "{0}" doesn\'t exist, and there is no local '
33-
'branch "{1}"'.format(remote, branch_name))
43+
'{1} "{2}"'.format(remote, ref_type_cap, ref_name))
3444

35-
b = r.lookup_branch(remote_branch)
36-
if not b:
37-
raise ValueError('Branch "{0}" doesn\'t exist in remote "{1}"'.format(
38-
remote_branch, remote))
39-
return b
45+
r = getattr(remote_repo, "lookup_" + ref_type)(remote_ref)
46+
if not r:
47+
raise ValueError('{0} "{1}" doesn\'t exist in remote "{2}"'.format(
48+
ref_type_cap, remote_ref, remote))
49+
return r
4050

4151

4252
def get_branch_or_use_upstream(branch_name, arg, repo):

gitless/cli/pprint.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77

88
from __future__ import unicode_literals
99

10+
try:
11+
from StringIO import StringIO
12+
except ImportError:
13+
from io import StringIO
14+
1015
from datetime import datetime, tzinfo, timedelta
1116
from locale import getpreferredencoding
1217
import re
@@ -124,6 +129,12 @@ def get_user_input(text='> '):
124129
return input(text)
125130

126131

132+
def commit_str(ci):
133+
ci_str = StringIO()
134+
commit(ci, compact=True, stream=ci_str.write)
135+
return ci_str.getvalue().strip()
136+
137+
127138
def commit(ci, compact=False, stream=sys.stdout.write):
128139
merge_commit = len(ci.parent_ids) > 1
129140
color = colored.magenta if merge_commit else colored.yellow

0 commit comments

Comments
 (0)