stg 0.1.5 → 0.1.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f993bbd2bd4575637beb24485c54a88a2679551000551df0f1b1a552249922f
4
- data.tar.gz: cca41c04c1ab146a16055debbf6364081993d4afb647222865a219d88d9a40bb
3
+ metadata.gz: 1eb93a1f37a9ec874a195189c9047b322ef7eb823f2c4af4219dad4aba1bdcb0
4
+ data.tar.gz: 7795502739c3503dcc0fa241cecc65093c711eed608d633ef32813c96c0f4d80
5
5
  SHA512:
6
- metadata.gz: 745cc975c54fc9e1449a514c711ca2a3ab98df951deb554b6e37de8f5e01ddbacf4e2001a20d0cd512d4ed5411291b4a213e0293928878b805ee5395acbd02c7
7
- data.tar.gz: 9afe52e540571bd3330743229d8e267150d243a9e28a815a503841199979c1a156434ad07b4a4d828a3f5db042710a3a5756999c8bf525a10aafa17f3dd0ff26
6
+ metadata.gz: 60c435caaec0c696b269a1cd014a96539fa646eb6c204a9b53d67cb55dda4f2de16813202e3e1c6e5a3a12d774acf1c43cb26eb418c1817b03cca7d242b2aded
7
+ data.tar.gz: e1b69a714e519862c46d4264648504f3699b94fc7046f9900dbe780e36f3db8fa255cc9dfe40323b201bcb6f18ea7a8a0f01409b23724eedda67d7fa665e8d5e
data/README.md CHANGED
@@ -12,17 +12,22 @@ Stolen git is well... you get it. I'm building a mini git clone to learn version
12
12
 
13
13
  ## Usage
14
14
 
15
- | Command | description | Flags |
16
- | ------- | ----------------------------------------------------------------- | --------------------------------------- |
17
- | init | Initialize the project | N/A |
18
- | stage | add a file or directory to be tracked | N/A |
19
- | commit | Save the current tracked state | `-n [--name]` <br> `-d [--description]` |
20
- | diff | get the difference between working directory and the last commit | N/A |
21
- | log | print out commit history (limit print by a number `log <number>`) | N/A |
22
-
23
- | reset | Reset to the last commit | N/A |
24
- | checkout | check a commit or a branch without loss in data | `-c [--commit] <commit_id>` to check out a commit instaed of a branch wihout changing HEAD pointer |
25
- | branch | List all branches. (or creating a branch by `branch <name>`) | N/A |
15
+ Run `stg init` once in a project before using the other commands. Stolen Git stores its data in `.stolen-git/` and tracks files through its own index.
16
+
17
+ | Command | Description | Options |
18
+ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
19
+ | `init` | Initialize `.stolen-git/` in the current directory. | N/A |
20
+ | `stage <file...>` | Add files or directories to the Stolen Git index. Directories are staged recursively. | N/A |
21
+ | `commit` | Save the current indexed state as a commit. | `-n, --name NAME` <br> `-d, --description DESCRIPTION` |
22
+ | `diff` | Show differences between the working directory and the last commit. | N/A |
23
+ | `log [limit]` | Print commit history. Pass a limit to show only the latest entries. | N/A |
24
+ | `reset [commit_id]` | With no id, restore working files from the index. With an id, restore that commit and move the current branch pointer. | N/A |
25
+ | `checkout <name>` | Check out a branch by name. | N/A |
26
+ | `checkout -c <commit_id>` | Check out a commit without moving the current branch pointer. | `-c, --commit` |
27
+ | `branch [name]` | List all branches, or create a branch when a name is provided. | N/A |
28
+ | `help` | Print the command list. | N/A |
29
+
30
+ For more detail on staging, committing, and reset behavior, see [COMMANDS.md](COMMANDS.md).
26
31
 
27
32
  ## Installation
28
33
 
@@ -39,6 +44,9 @@ Stolen git is well... you get it. I'm building a mini git clone to learn version
39
44
 
40
45
  ```
41
46
  gem install stg
47
+
48
+ # you may need administrative permission, in this case
49
+ sudo gem install stg
42
50
  ```
43
51
 
44
52
  Run `stg` to verify your installation
@@ -48,47 +56,51 @@ Run `stg` to verify your installation
48
56
  > [!NOTE]
49
57
  > You have to initialize with `stg init` for any of the other commands to work
50
58
 
51
- - Initialization
52
-
53
- ```
54
- stg init
55
- ```
59
+ ### Start a new project
56
60
 
57
- - Staging
58
-
59
- ```
60
- stg stage src/page.ts
61
- ```
61
+ ```sh
62
+ stg init
63
+ stg stage .
64
+ stg commit -n "Initial commit"
65
+ ```
62
66
 
63
- - Committing
67
+ ### Save a file change
64
68
 
65
- ```
66
- stg commit -n "Adding main page"
67
- ```
69
+ ```sh
70
+ stg stage lib/stg/actions.rb
71
+ stg commit -n "Improve reset validation"
72
+ ```
68
73
 
69
- - Logging all commits
74
+ ### Inspect history
70
75
 
71
- ```
72
- stg log
73
- ```
76
+ ```sh
77
+ stg log # Show 5 logs and waits for user confirmation to continue
78
+ stg log 3 # Shows last 3 commits in the branch and closes
79
+ ```
74
80
 
75
- Logging last 5 commits
81
+ ### Discard unstaged working changes
76
82
 
77
- ```
78
- stg log 5
79
- ```
83
+ ```sh
84
+ stg stage README.md
85
+ # edit README.md again
86
+ stg reset
87
+ ```
80
88
 
81
- - Help
89
+ `README.md` is restored to the version stored in the index (a.k.a last version of the file you staged).
82
90
 
83
- ```
84
- stg
85
- or
86
- stg help
87
- ```
91
+ ### Reset to a previous commit
88
92
 
89
- - Hard reset
93
+ > [!WARNING]
94
+ > `stg reset <commit_id>` is destructive. It moves the branch pointer back and can make later Stolen Git commits unreachable. Use `stg checkout -c <commit_id>` if you only want to inspect an older commit.
90
95
 
96
+ ```sh
97
+ stg log # shows all commits with commit_id next to the word commit in green
98
+ stg reset <commit_id>
91
99
  ```
92
100
 
101
+ ### Help
102
+
103
+ ```sh
93
104
  stg
105
+ stg help
94
106
  ```
data/lib/stg/actions.rb CHANGED
@@ -96,11 +96,27 @@ module Actions
96
96
 
97
97
  index = read_json('.stolen-git/index.json')
98
98
  ignore = read_json('.stg-ignore')
99
- stage_file = lambda do |file_path|
100
- ignore&.each do |ignore_pattern|
101
- return if File.fnmatch(ignore_pattern, file_path)
99
+ index.reject! { |key, _value| ignored_path?(key, ignore) }
100
+
101
+ path_in_directory = lambda do |path, dir_path|
102
+ dir_path == '.' || path == dir_path || path.start_with?("#{dir_path}/")
103
+ end
104
+
105
+ stage_deleted = lambda do |path|
106
+ path = clean_path(path)
107
+
108
+ index.keys.each do |indexed_path|
109
+ next unless indexed_path == path || path_in_directory.call(indexed_path, path)
110
+ next if File.exist?(indexed_path)
111
+
112
+ index.delete(indexed_path)
102
113
  end
114
+ end
115
+
116
+ stage_file = lambda do |file_path|
103
117
  file_path = clean_path(file_path)
118
+ return if ignored_path?(file_path, ignore)
119
+
104
120
  file_hash = get_file_hash(file_path)
105
121
  file_content = File.read(file_path)
106
122
 
@@ -117,6 +133,10 @@ module Actions
117
133
 
118
134
  stage_directory = lambda do |dir_path|
119
135
  dir_path = clean_path(dir_path)
136
+ return if ignored_path?(dir_path, ignore)
137
+
138
+ stage_deleted.call(dir_path)
139
+
120
140
  Dir.children(dir_path).each do |entry|
121
141
  next if entry == '.stolen-git'
122
142
 
@@ -132,7 +152,11 @@ module Actions
132
152
  inp.each do |inp_path|
133
153
  inp_path = clean_path(inp_path)
134
154
  unless File.exist? inp_path
135
- puts "#{inp_path} doesn't exist"
155
+ if index.key?(inp_path) || index.keys.any? { |key| path_in_directory.call(key, inp_path) }
156
+ stage_deleted.call(inp_path)
157
+ else
158
+ puts "#{inp_path} doesn't exist"
159
+ end
136
160
  next
137
161
  end
138
162
 
@@ -182,6 +206,11 @@ module Actions
182
206
 
183
207
  # Get the index
184
208
  index = JSON.parse(File.read('.stolen-git/index.json'))
209
+ ignore = read_json('.stg-ignore')
210
+ original_index_size = index.length
211
+ index.reject! { |key, _value| ignored_path?(key, ignore) }
212
+ File.write('.stolen-git/index.json', JSON.pretty_generate(index)) if index.length != original_index_size
213
+
185
214
  tree_content = {
186
215
  entries: []
187
216
  }
@@ -213,7 +242,9 @@ module Actions
213
242
  commit_diff[key] = diff
214
243
  end
215
244
  else
216
- parent_map = entries.to_h { |e| [e['path'], e['hash']] }
245
+ parent_map = entries
246
+ .reject { |entry| ignored_path?(entry['path'], ignore) }
247
+ .to_h { |e| [e['path'], e['hash']] }
217
248
 
218
249
  index.each do |key, value|
219
250
  key = clean_path(key)
@@ -321,9 +352,14 @@ module Actions
321
352
  end
322
353
 
323
354
  def reset
355
+ reset_usage = lambda do
356
+ puts 'Usage: stg reset <commit_id>'
357
+ puts "You can find commit_id by running 'stg log'"
358
+ end
359
+
324
360
  begin
325
361
  OptionParser.new do |opts|
326
- opts.banner = 'Usage: stg reset [commit_id]'
362
+ opts.banner = 'Usage: stg reset <commit_id>'
327
363
 
328
364
  opts.on_tail('-h', '--help', 'Show this help') do
329
365
  puts opts
@@ -332,28 +368,30 @@ module Actions
332
368
  end.parse!
333
369
  rescue OptionParser::ParseError => e
334
370
  puts e.message
335
- puts 'Usage: stg reset [commit_id]'
371
+ reset_usage.call
336
372
  puts ' -h, --help Show this help'
337
373
  exit 1
338
374
  end
339
375
 
376
+ if ARGV.length > 1
377
+ reset_usage.call
378
+ return
379
+ end
380
+
340
381
  commit_id = ARGV.first
341
382
  commit_history = read_json('.stolen-git/commits.json')
342
383
  pointer = read_json('.stolen-git/pointer.json')
343
384
  branch_id = pointer['point_to']
344
385
  branch_content = read_json(".stolen-git/branches/#{branch_id}.json")
345
- current_commit_hash = branch_content['commit_pointer']
346
-
347
- new_commit = if !commit_id || commit_id.empty?
348
- current_commit_hash
349
- else
350
- commit_history['commits'].find do |x|
351
- x['id'] == commit_id
352
- end['hash']
353
- end
354
-
355
- if new_commit.empty?
356
- puts "This commit doesn't exit"
386
+ if commit_id.nil? || commit_id.empty?
387
+ revert_to_index
388
+ return
389
+ end
390
+
391
+ commit = commit_history['commits'].find { |x| x['id'] == commit_id }
392
+ new_commit = commit && commit['hash']
393
+ if new_commit.nil? || new_commit.empty?
394
+ reset_usage.call
357
395
  return
358
396
  end
359
397
 
@@ -83,7 +83,8 @@ module DiffCalc
83
83
  deletion_seq[i2].push({ old_index: i1, ma_type: 'bs' })
84
84
  i1 += 1
85
85
  when :insert
86
- insertion_seq[i2] = { value: @new_s[i2].encode('UTF-8', invalid: :replace, undef: :replace, replace: ''), old_index: i1 }
86
+ insertion_seq[i2] =
87
+ { value: @new_s[i2].encode('UTF-8', invalid: :replace, undef: :replace, replace: ''), old_index: i1 }
87
88
  i2 += 1
88
89
  end
89
90
  end
data/lib/stg/help.rb CHANGED
@@ -5,14 +5,14 @@ module Help
5
5
  Commands:\n"
6
6
  commands_docs = {
7
7
  init: 'Initialize the project',
8
- stage: 'add a file or directory to be tracked ',
9
- commit: 'Save the current tracked state ',
10
- diff: 'get the difference between working directory and the last commit ',
11
- log: 'print out commit history',
12
- reset: 'Revert to commit',
13
- checkout: 'check a commit or a branch without loss in data',
14
- branch: 'List all branches.',
15
- help: 'show this list'
8
+ stage: 'Add files or directories to the index',
9
+ commit: 'Save the current indexed state',
10
+ diff: 'Show differences between the working directory and the last commit',
11
+ log: 'Print commit history',
12
+ reset: 'Restore from the index, or restore a commit when an id is provided',
13
+ checkout: 'Check out a branch, or a commit with -c',
14
+ branch: 'List branches, or create a branch when a name is provided',
15
+ help: 'Show this list'
16
16
  }
17
17
  max_len = 0
18
18
  commands_docs.each_key do |command|
data/lib/stg/utils.rb CHANGED
@@ -50,22 +50,52 @@ module Utils
50
50
  def revert_to_commit(commit_hash)
51
51
  commit_content = read_json(".stolen-git/commits/#{commit_hash}.json")
52
52
  commit_tree = read_json(".stolen-git/storage/trees/#{commit_content['tree_hash']}.json")
53
+ current_index = read_json('.stolen-git/index.json') || {}
53
54
  index = {}
55
+
54
56
  commit_tree['entries'].each do |entry|
55
57
  blob = File.read(".stolen-git/storage/blobs/#{entry['hash']}")
56
58
  # TODO: Figure out what to do when path changes
59
+ dir = File.dirname(entry['path'])
60
+ FileUtils.mkdir_p(dir) unless dir == '.'
57
61
  File.write(entry['path'], blob)
58
62
  index[entry['path']] ||= {}
59
63
  index[entry['path']]['hash'] = entry['hash']
60
64
  end
61
65
 
66
+ (current_index.keys - index.keys).each do |path|
67
+ FileUtils.rm_f(path)
68
+ end
69
+
62
70
  File.write('.stolen-git/index.json', JSON.pretty_generate(index))
63
71
  end
64
72
 
73
+ def revert_to_index
74
+ index = read_json('.stolen-git/index.json') || {}
75
+
76
+ index.each do |path, entry|
77
+ blob = File.read(".stolen-git/storage/blobs/#{entry['hash']}")
78
+ File.write(path, blob)
79
+ end
80
+ end
81
+
65
82
  def clean_path(path)
66
83
  Pathname.new(path).cleanpath.to_s
67
84
  end
68
85
 
86
+ def ignored_path?(path, ignore_patterns)
87
+ path = clean_path(path)
88
+ return false if path == '.'
89
+
90
+ path_as_dir = path.end_with?('/') ? path : "#{path}/"
91
+
92
+ ignore_patterns&.any? do |pattern|
93
+ File.fnmatch(pattern, path) ||
94
+ File.fnmatch(pattern, path_as_dir) ||
95
+ (pattern.end_with?('/') && File.fnmatch("#{pattern}**", path))
96
+ end
97
+ end
98
+
69
99
  def check_program_exists
70
100
  return true if File.exist? '.stolen-git'
71
101
 
data/lib/stg/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Stg
2
- VERSION = '0.1.5'
2
+ VERSION = '0.1.6'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amr ElTaweel