This repository was archived by the owner on Mar 11, 2026. It is now read-only.
forked from LFDT-Nightstream/Nightstream
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathshow_diff.sh
More file actions
executable file
·317 lines (290 loc) · 10.9 KB
/
show_diff.sh
File metadata and controls
executable file
·317 lines (290 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
#!/bin/bash
# Script to save diff between current changes and a base commit to a file
# Usage: ./show_diff.sh [output_file] ["path1,path2,path3,..."] [base_commit] [--no-tests]
#
# Examples:
# ./show_diff.sh # Save all changes to 'git_diff.txt' (vs HEAD)
# ./show_diff.sh my_changes.txt # Save all changes to 'my_changes.txt' (vs HEAD)
# ./show_diff.sh changes.txt "src,tests" # Save only src/ and tests/ changes (vs HEAD)
# ./show_diff.sh diff.txt "crates/neo-fold,neo-main/src" # Save specific paths only (vs HEAD)
# ./show_diff.sh changes.txt "" "main" # Save all changes vs main branch
# ./show_diff.sh changes.txt "src" "abc123" # Save src/ changes vs commit abc123
# ./show_diff.sh changes.txt "" "HEAD" --no-tests # Save all changes excluding test files
# ./show_diff.sh --no-tests # Save all changes excluding test files to 'git_diff.txt'
# Parse arguments and handle --no-tests flag
no_tests=false
args=()
# Process all arguments to separate --no-tests from positional args
for arg in "$@"; do
if [ "$arg" = "--no-tests" ]; then
no_tests=true
elif [ "$arg" = "--help" ] || [ "$arg" = "-h" ]; then
echo "Usage: $0 [output_file] [\"path1,path2,path3,...\"] [base_commit] [--no-tests]"
echo ""
echo "Examples:"
echo " $0 # Save all changes to 'git_diff.txt' (vs HEAD)"
echo " $0 my_changes.txt # Save all changes to 'my_changes.txt' (vs HEAD)"
echo " $0 changes.txt \"src,tests\" # Save only src/ and tests/ changes (vs HEAD)"
echo " $0 diff.txt \"crates/neo-fold,neo-main/src\" # Save specific paths only (vs HEAD)"
echo " $0 changes.txt \"\" \"main\" # Save all changes vs main branch"
echo " $0 changes.txt \"src\" \"abc123\" # Save src/ changes vs commit abc123"
echo " $0 changes.txt \"\" \"HEAD\" --no-tests # Save all changes excluding test files"
echo " $0 --no-tests # Save all changes excluding test files to 'git_diff.txt'"
echo ""
echo "Options:"
echo " --no-tests Exclude files in test directories (/tests/, /test/, *_test.*, test_*)"
echo " --help, -h Show this help message"
exit 0
else
args+=("$arg")
fi
done
# Default values using processed arguments
output_file="${args[0]:-git_diff.txt}"
target_paths="${args[1]:-}"
base_commit="${args[2]:-HEAD}"
# Parse comma-separated paths into array
if [ -n "$target_paths" ]; then
IFS=',' read -ra paths <<< "$target_paths"
# Remove trailing slashes from paths
for i in "${!paths[@]}"; do
paths[$i]="${paths[$i]%/}"
done
else
paths=()
fi
# Function to filter out test files from a list of files
filter_test_files() {
local files="$1"
if [ "$no_tests" = true ]; then
echo "$files" | grep -v '/tests/' | grep -v '/test/' | grep -v '_test\.' | grep -v 'test_'
else
echo "$files"
fi
}
# Return modified files against the selected base/path filters, one per line.
collect_modified_files() {
local base="$1"
local files
if [ ${#paths[@]} -gt 0 ]; then
files=$(git diff "$base" --name-only -- "${paths[@]}")
else
files=$(git diff "$base" --name-only)
fi
if [ "$no_tests" = true ] && [ -n "$files" ]; then
files=$(filter_test_files "$files")
fi
echo "$files" | sed '/^$/d'
}
# Count non-empty newline-separated entries.
count_nonempty_lines() {
local lines="$1"
if [ -z "$lines" ]; then
echo 0
else
echo "$lines" | sed '/^$/d' | wc -l | tr -d ' '
fi
}
# Compute total added/deleted lines for a newline-separated file list.
# Outputs: "<adds> <deletes>"
compute_add_delete_totals() {
local base="$1"
local files="$2"
local total_add=0
local total_del=0
if [ -n "$files" ]; then
while IFS= read -r file; do
[ -z "$file" ] && continue
while IFS=$'\t' read -r added deleted _; do
[ -z "$added" ] && continue
[ "$added" = "-" ] && added=0
[ "$deleted" = "-" ] && deleted=0
total_add=$((total_add + added))
total_del=$((total_del + deleted))
done <<< "$(git diff --numstat "$base" -- "$file")"
done <<< "$files"
fi
echo "$total_add $total_del"
}
# Remove existing output file
rm -f "$output_file"
touch "$output_file"
# Validate base commit exists
if ! git rev-parse --verify "$base_commit" >/dev/null 2>&1; then
echo "Error: Base commit '$base_commit' not found or invalid."
echo "Please provide a valid commit hash, branch name, or tag."
exit 1
fi
echo "Generating diff report..."
if [ "$base_commit" != "HEAD" ]; then
echo "Using base commit: $base_commit"
fi
if [ ${#paths[@]} -gt 0 ]; then
echo "Including only specified paths: ${paths[*]}"
fi
if [ "$no_tests" = true ]; then
echo "Excluding test files (--no-tests flag active)"
fi
# Collect modified files/stats once so all sections are consistent.
modified_files=$(collect_modified_files "$base_commit")
modified_count=$(count_nonempty_lines "$modified_files")
read -r total_added_lines total_deleted_lines <<< "$(compute_add_delete_totals "$base_commit" "$modified_files")"
# Header
{
echo "=============================================="
echo "Git Diff Report - $(date)"
echo "=============================================="
echo "Branch: $(git branch --show-current)"
echo "Commit: $(git rev-parse --short HEAD)"
echo "Base commit: $(git rev-parse --short $base_commit)"
echo ""
} >> "$output_file"
# Git Status Summary
{
echo "=============================================="
echo "Git Status Summary"
echo "=============================================="
if [ ${#paths[@]} -gt 0 ]; then
status_output=$(git status --short -- "${paths[@]}")
else
status_output=$(git status --short)
fi
if [ "$no_tests" = true ] && [ -n "$status_output" ]; then
status_output=$(filter_test_files "$status_output")
fi
echo "$status_output"
echo ""
} >> "$output_file"
# Diff of Modified Files
{
echo "=============================================="
echo "Diff of Modified Files (against $base_commit)"
echo "=============================================="
if [ -n "$modified_files" ]; then
echo "$modified_files" | while IFS= read -r file; do
if [ -n "$file" ]; then
git diff "$base_commit" -- "$file"
fi
done
else
echo "No modified files found."
fi
echo ""
} >> "$output_file"
# Untracked Files Content
{
echo "=============================================="
echo "Untracked Files Content"
echo "=============================================="
# Get untracked files, filtered by paths if specified
if [ ${#paths[@]} -gt 0 ]; then
untracked_files=""
for path in "${paths[@]}"; do
if [ -d "$path" ]; then
path_untracked=$(git ls-files --others --exclude-standard "$path/")
elif [ -f "$path" ]; then
# Check if the specific file is untracked
if git ls-files --error-unmatch "$path" >/dev/null 2>&1; then
path_untracked=""
else
path_untracked="$path"
fi
else
path_untracked=""
fi
if [ -n "$path_untracked" ]; then
untracked_files="$untracked_files$path_untracked"$'\n'
fi
done
untracked_files=$(echo "$untracked_files" | grep -v '^$')
else
untracked_files=$(git ls-files --others --exclude-standard)
fi
# Apply test file filtering to untracked files
if [ "$no_tests" = true ] && [ -n "$untracked_files" ]; then
untracked_files=$(filter_test_files "$untracked_files")
fi
if [ -n "$untracked_files" ]; then
echo "Untracked files found:"
echo "$untracked_files"
echo ""
echo "$untracked_files" | while IFS= read -r file; do
if [ -f "$file" ]; then
# Only show content for small files (less than 100KB)
file_size=$(wc -c < "$file" 2>/dev/null || echo 0)
if [ "$file_size" -lt 102400 ]; then
echo "--- Content of $file ---"
cat "$file"
echo ""
echo "--- End of $file ---"
echo ""
else
echo "--- $file (too large to display, $(($file_size / 1024))KB) ---"
echo ""
fi
fi
done
else
echo "No untracked files found."
fi
echo ""
} >> "$output_file"
# Summary
{
echo "=============================================="
echo "Summary"
echo "=============================================="
untracked_count=0
if [ -n "$untracked_files" ]; then
untracked_count=$(echo "$untracked_files" | wc -l)
fi
# Calculate file statistics for the summary
if [ -f "$output_file" ]; then
summary_size=$(wc -c < "$output_file")
summary_lines=$(wc -l < "$output_file")
summary_words=$(wc -w < "$output_file")
# Approximate AI token count (1 token ≈ 4 characters for English text)
summary_ai_tokens=$((summary_size / 4))
else
summary_size=0
summary_lines=0
summary_words=0
summary_ai_tokens=0
fi
echo "Modified files: $modified_count"
echo "Lines added (+): $total_added_lines"
echo "Lines deleted (-): $total_deleted_lines"
echo "Untracked files: $untracked_count"
if [ ${#paths[@]} -gt 0 ]; then
echo "Filtered paths: ${paths[*]}"
fi
if [ "$no_tests" = true ]; then
echo "Test files excluded: --no-tests flag active"
fi
echo ""
echo "File Statistics:"
echo "Total size: ${summary_size} bytes"
echo "Total lines: ${summary_lines}"
echo "Total words: ${summary_words}"
echo "Total AI tokens (est): ${summary_ai_tokens}"
} >> "$output_file"
# Show final statistics
if [ -f "$output_file" ]; then
final_size=$(wc -c < "$output_file")
final_lines=$(wc -l < "$output_file")
final_words=$(wc -w < "$output_file")
# Approximate AI token count (1 token ≈ 4 characters for English text)
final_ai_tokens=$((final_size / 4))
echo
echo "=== Diff Report Generated ==="
echo "Output file: $output_file"
echo "Lines added (+): ${total_added_lines}"
echo "Lines deleted (-): ${total_deleted_lines}"
echo "Total size: ${final_size} bytes"
echo "Total lines: ${final_lines}"
echo "Total words: ${final_words}"
echo "Total AI tokens (est): ${final_ai_tokens}"
echo
echo "View the diff with: cat $output_file"
echo "Or open in editor: nano $output_file"
fi