Skip to content

Commit 3c2dea3

Browse files
committed
add reset command
1 parent d8dc03a commit 3c2dea3

8 files changed

Lines changed: 132 additions & 47 deletions

File tree

Schedule.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
* commit
55
* rm
66
* log
7-
* status(with .gitignore support)
7+
* status
88
* branch
9+
* reset
910

1011
###Doing
1112
* push
@@ -14,5 +15,4 @@
1415
* clone
1516
* diff
1617
* merge
17-
18-
......
18+
* ......

src/branch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import shutil
88

99
from constants import HEAD_PATH, REF_HEADS_DIR
10-
from utils import read_file, get_all_files_in_dir
10+
from utils import read_file
1111

1212

1313
class Branch(object):

src/command.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ def cmd_branch(name, is_deleted):
5858
else :
5959
repo.branch.add_branch(name)
6060

61+
@staticmethod
62+
def cmd_reset(commit_sha1, is_soft, is_hard):
63+
repo = Repository()
64+
repo.update_head_commit(commit_sha1)
65+
if not is_soft:
66+
repo.rebuild_index_from_commit(commit_sha1)
67+
if is_hard:
68+
repo.rebuild_working_tree()
69+
6170
@staticmethod
6271
def cmd_push():
6372
pass

src/git.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,35 @@ def __init__(self, argv):
145145
}
146146
],
147147
},
148+
'reset' : {
149+
'func' : self._reset,
150+
'help' : 'Reset current HEAD to the specified state',
151+
'args' : [
152+
{
153+
'name' : ['commit_sha1'],
154+
'properties' :
155+
{
156+
'help' : 'Sha1 of a commit',
157+
}
158+
},
159+
{
160+
'name' : ['--soft'],
161+
'properties' :
162+
{
163+
'help' : 'Does not touch the index file or the working tree at all',
164+
'action' : 'store_true',
165+
}
166+
},
167+
{
168+
'name' : ['--hard'],
169+
'properties' :
170+
{
171+
'help' : 'Resets the index and working tree',
172+
'action' : 'store_true',
173+
}
174+
},
175+
],
176+
},
148177
'push' : {
149178
'func' : self._push,
150179
'help' : 'Update remote refs along with associated objects',
@@ -180,6 +209,9 @@ def _status(self, args):
180209
def _branch(self, args):
181210
Command.cmd_branch(args.name, args.is_deleted)
182211

212+
def _reset(self, args):
213+
Command.cmd_reset(args.commit_sha1, is_soft=args.soft, is_hard=args.hard)
214+
183215
def _push(self, args):
184216
pass
185217

src/index.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import struct
1111

1212
from objects import Tree
13-
from utils import Sha1Reader, Sha1Writer, write_object_to_file
13+
from utils import Sha1Reader, Sha1Writer, write_to_file
1414

1515

1616
class Index(object):
@@ -33,7 +33,7 @@ def _parse_header(self, f):
3333
return entries_num
3434

3535

36-
def add_entry(self, name, **kwargs):
36+
def set_entry(self, name, **kwargs):
3737
self.entries[name] = kwargs
3838

3939
def _parse_entries(self, f):
@@ -46,7 +46,7 @@ def _parse_entries(self, f):
4646
real_size = ((f.tell() - begin + 8) & ~7)
4747
f.read((begin + real_size) - f.tell())
4848

49-
self.add_entry(name, ctime=ctime, mtime=mtime, dev=dev, ino=ino, mode=mode, \
49+
self.set_entry(name, ctime=ctime, mtime=mtime, dev=dev, ino=ino, mode=int(mode), \
5050
uid=uid, gid=gid, size=size,sha1=binascii.hexlify(sha1), flags=flags & ~0x0fff)
5151

5252
def _parse_file(self):
@@ -116,7 +116,7 @@ def _build_tree(path):
116116
(mode, sha1) = entry
117117
file_arr.append({'name':name, 'mode':mode, 'sha1':sha1})
118118
newtree = Tree(sorted(dir_arr,key = lambda x:x['name']) + sorted(file_arr,key = lambda x:x['name']))
119-
write_object_to_file(newtree.path, newtree.content)
119+
write_to_file(newtree.path, newtree.content)
120120
return newtree
121121

122122
return _build_tree(tree)

src/objects.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from utils import cal_sha1, read_file
1515

1616

17+
1718
class BaseObject(object):
1819
'''
1920
git base object
@@ -34,16 +35,21 @@ def __init__(self, final_content=None, sha1=None):
3435
class Blob(BaseObject):
3536

3637
def __init__(self, raw_content=None, sha1=None):
37-
final_content = 'blob %d\0%s' % (len(raw_content), raw_content)
38-
super(Blob, self).__init__(final_content)
38+
if sha1:
39+
super(Blob, self).__init__(sha1=sha1)
40+
final_content = zlib.decompress(self.content)
41+
self.raw_content = final_content[final_content.find('\0') + 1:]
42+
else:
43+
final_content = 'blob %d\0%s' % (len(raw_content), raw_content)
44+
super(Blob, self).__init__(final_content)
3945

4046
class Tree(BaseObject):
4147
def __init__(self, args=None, sha1=None):
4248
if sha1:
4349
super(Tree, self).__init__(sha1=sha1)
4450
final_content = zlib.decompress(self.content)
4551
self.raw_content = final_content[final_content.find('\0') + 1:]
46-
self.objects = re.findall('(\d+) (\S+)\0(.{20})', self.raw_content, re.S)
52+
self.objects = re.findall('(\d+) ([^\0]+)\0(.{20})', self.raw_content, re.S)
4753

4854
else:
4955
raw_content = ''
@@ -63,7 +69,7 @@ def parse_objects(self):
6369
for new_object in new_objects:
6470
queue.append([new_object[0], os.path.join(name, new_object[1]), new_object[2]])
6571
else:
66-
res[name] = {'mode' : mode, 'sha1' : sha1}
72+
res[name] = {'mode' : int(mode, 8), 'sha1' : sha1}
6773
return res
6874

6975

src/repository.py

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
INIT_FILE, CONFIG_PATH
1515
from index import Index
1616
from objects import Blob, Commit, Tree
17-
from utils import read_file, write_to_file, cal_mode, write_object_to_file, \
18-
less_str, get_all_files_in_dir, get_file_mode, filter_by_gitignore
17+
from utils import get_all_files_in_dir, read_file, write_to_file, cal_mode, \
18+
less_str, filter_by_gitignore, get_file_mode
1919

2020

2121
class Repository(object):
@@ -24,7 +24,7 @@ class Repository(object):
2424
'''
2525
def __init__(self):
2626
self.index = Index(INDEX_PATH)
27-
self.all_files = get_all_files_in_dir('.', GIT_DIR)
27+
self.working_tree_files = get_all_files_in_dir('.', GIT_DIR)
2828
self.config = Config()
2929
self.branch = Branch()
3030

@@ -34,11 +34,12 @@ def stage(self, files):
3434
content = read_file(file)
3535
blob = Blob(content)
3636
if not os.path.exists(blob.path):
37-
write_object_to_file(blob.path, blob.content)
37+
write_to_file(blob.path, blob.content)
3838
stat = os.stat(os.path.join(file))
39-
self.index.add_entry(file, ctime=stat.st_ctime, mtime=stat.st_mtime, dev=stat.st_dev, ino=stat.st_ino, mode=cal_mode(stat.st_mode), \
39+
self.index.set_entry(file, ctime=stat.st_ctime, mtime=stat.st_mtime, dev=stat.st_dev, ino=stat.st_ino, mode=cal_mode(stat.st_mode), \
4040
uid=stat.st_uid, gid=stat.st_gid, size=stat.st_size, sha1=blob.sha1, flags=0)
4141
self.index.write_to_file()
42+
self.index = Index(INDEX_PATH)
4243

4344
except Exception, e:
4445
print 'stage file %s error: %s' % (file, e)
@@ -83,7 +84,7 @@ def commit(self, msg):
8384

8485
commit = Commit(sha1=None, tree_sha1=new_tree.sha1, parent_sha1=self.branch.head_commit, name=committer_name, email=committer_email, \
8586
timestamp=commit_time, timezone=commit_timezone, msg=msg)
86-
write_object_to_file(commit.path, commit.content)
87+
write_to_file(commit.path, commit.content)
8788
write_to_file(self.branch.head_path, commit.sha1)
8889

8990
def delete(self, file):
@@ -101,7 +102,7 @@ def show_log(self, num):
101102
less_str(print_str)
102103

103104
def _get_untracked_files(self):
104-
raw_list = list(set(self.all_files).difference(set(list(self.index.entries))))
105+
raw_list = list(set(self.working_tree_files).difference(set(list(self.index.entries))))
105106
return filter_by_gitignore(raw_list)
106107

107108
def _get_unstaged_files(self):
@@ -110,7 +111,7 @@ def _get_unstaged_files(self):
110111
'deleted' : [],
111112
}
112113
for name, properties in self.index.entries.iteritems():
113-
if name not in self.all_files:
114+
if name not in self.working_tree_files:
114115
res['deleted'].append(name)
115116
elif get_file_mode(name) != properties['mode'] or Blob(read_file(name)).sha1 != properties['sha1']:
116117
res['modified'].append(name)
@@ -120,14 +121,14 @@ def _get_unstaged_files(self):
120121

121122
def _get_uncommitted_files(self):
122123
if not self.branch.head_commit:
123-
return {}
124+
return {'new file' : self.index.entries}
124125

125126
tree = Tree(sha1=Commit(sha1=self.branch.head_commit).tree)
126127
tree_objects = tree.parse_objects()
127128
return {
128129
'modified': [name for name in set(self.index.entries).intersection(set(tree_objects)) \
129130
if self.index.entries[name]['sha1'] != tree_objects[name]['sha1'] or \
130-
int(oct(self.index.entries[name]['mode'])) != int(tree_objects[name]['mode'])],
131+
self.index.entries[name]['mode'] != tree_objects[name]['mode']],
131132
'deleted' : set(tree_objects).difference(self.index.entries),
132133
'new file' : set(self.index.entries).difference(set(tree_objects)),
133134
}
@@ -137,23 +138,58 @@ def show_status(self):
137138
unstaged_files = self._get_unstaged_files()
138139
uncommitted_files = self._get_uncommitted_files()
139140
print_str = 'On branch %s\n' % (self.branch.head_name)
140-
141-
print_str += 'Changes to be committed:\n (use "git reset HEAD <file>..." to unstage)\n\n'
142-
for change, files in uncommitted_files.iteritems():
143-
for file in files:
144-
print_str += colored('\t%s:\t%s\n' % (change, file), 'green')
145-
print_str += '\n'
146-
147-
print_str += 'Changes not staged for commit:\n (use "git add <file>..." to update what will be committed)\n'
148-
print_str += ' (use "git checkout -- <file>..." to discard changes in working directory)\n\n'
149-
for change, files in unstaged_files.iteritems():
150-
for file in files:
151-
print_str += colored('\t%s:\t%s\n' % (change, file), 'red')
152-
print_str += '\n'
153-
154-
print_str += 'Untracked files:\n (use "git add <file>..." to include in what will be committed)\n\n'
155-
for file in untracked_files:
156-
print_str += colored('\t%s\n' % file, 'red')
157-
print_str += '\n'
141+
142+
if uncommitted_files['modified'] or uncommitted_files['deleted'] or uncommitted_files['new file']:
143+
print_str += 'Changes to be committed:\n (use "git reset HEAD <file>..." to unstage)\n\n'
144+
for change, files in uncommitted_files.iteritems():
145+
for file in files:
146+
print_str += colored('\t%s:\t%s\n' % (change, file), 'green')
147+
print_str += '\n'
148+
149+
if unstaged_files['modified'] or unstaged_files['deleted']:
150+
print_str += 'Changes not staged for commit:\n (use "git add <file>..." to update what will be committed)\n'
151+
print_str += ' (use "git checkout -- <file>..." to discard changes in working directory)\n\n'
152+
for change, files in unstaged_files.iteritems():
153+
for file in files:
154+
print_str += colored('\t%s:\t%s\n' % (change, file), 'red')
155+
print_str += '\n'
156+
157+
if untracked_files:
158+
print_str += 'Untracked files:\n (use "git add <file>..." to include in what will be committed)\n\n'
159+
for file in untracked_files:
160+
print_str += colored('\t%s\n' % file, 'red')
161+
print_str += '\n'
158162

159163
print print_str
164+
165+
def update_head_commit(self, commit_sha1):
166+
write_to_file(self.branch.head_path, commit_sha1)
167+
168+
def rebuild_index_from_commit(self, commit_sha1):
169+
tree = Tree(sha1=Commit(sha1=commit_sha1).tree)
170+
tree_objects = tree.parse_objects()
171+
172+
for name in set(self.index.entries).difference(set(tree_objects)):
173+
self.index.entries.pop(name)
174+
175+
for name, properties in tree_objects.iteritems():
176+
if not self.index.entries.has_key(name) or properties['sha1'] != self.index.entries[name]['sha1'] or \
177+
properties['mode'] != self.index.entries[name]['mode']:
178+
self.index.set_entry(name, ctime=0.0, mtime=0.0, dev=0, ino=0, mode=properties['mode'], \
179+
uid=0, gid=0, size=0, sha1=properties['sha1'], flags=0)
180+
181+
self.index.write_to_file()
182+
self.index = Index(INDEX_PATH)
183+
184+
def rebuild_working_tree(self):
185+
for path, properties in self.index.entries.iteritems():
186+
content = Blob(sha1=properties['sha1']).raw_content
187+
write_to_file(path, content, mode=properties['mode'])
188+
189+
190+
191+
192+
193+
194+
195+

src/utils.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,17 @@
1616

1717
S_IFGITLINK = 0o160000
1818

19-
def write_to_file(path, content):
19+
def write_to_file(path, content, mode=None):
20+
dir = os.path.dirname(path)
21+
22+
if dir and not os.path.exists(dir):
23+
os.makedirs(dir)
24+
2025
with open(path, 'w') as f:
2126
f.write(content)
22-
23-
def write_object_to_file(path, content):
24-
dir = os.path.dirname(path)
25-
if not os.path.exists(dir):
26-
os.mkdir(dir)
27-
write_to_file(path, content)
27+
28+
if mode:
29+
os.chmod(path, mode)
2830

2931
def read_file(file_name):
3032
with open(file_name, 'r') as f:

0 commit comments

Comments
 (0)