geet-geet 0.2.0 → 0.4.0
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.
- package/LICENSE +24 -0
- package/README.md +9 -7
- package/bin/geet.sh +5 -0
- package/docs/CONTRIBUTING.md +49 -31
- package/lib/help.sh +2 -0
- package/lib/inspect.sh +660 -0
- package/lib/version.sh +0 -0
- package/lib/why.sh +22 -12
- package/package.json +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
|
2
|
+
|
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
+
distribute this software, either in source code form or as a compiled
|
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
+
means.
|
|
7
|
+
|
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
+
of this software dedicate any and all copyright interest in the
|
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
|
11
|
+
of the public at large and to the detriment of our heirs and
|
|
12
|
+
successors. We intend this dedication to be an overt act of
|
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
+
software under copyright law.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
For more information, please refer to <http://unlicense.org/>
|
package/README.md
CHANGED
|
@@ -9,11 +9,15 @@ Only Git's *view* of the filesystem changes.
|
|
|
9
9
|
|
|
10
10
|
## Why?
|
|
11
11
|
> “I built something useful, and I think that **SOME but not all of my code is re-usable**.
|
|
12
|
-
I want to publish some of my code for
|
|
12
|
+
I want to publish some of my code for others to use (or to re-use myself)...
|
|
13
13
|
but **I don't want to spend weeks refactoring** to split apart the reusable code from the implementation-specific code.
|
|
14
14
|
In fact, it may not even be possible to move around all my files without breaking things.
|
|
15
15
|
Plus, supporting this template is my **secondary task** which I want to do in tandem with my **primary development**, using my main repository's working directory and publishing some pieces to the template repo.”
|
|
16
16
|
|
|
17
|
+
## Why Not?
|
|
18
|
+
> If you can super cleanly separate your template from your app or make your sourcecode fully modular, you don't need geet, use a normal repo or maybe submodules.
|
|
19
|
+
|
|
20
|
+
## Why?
|
|
17
21
|
- **Making code re-usable is a struggle**, especially if you want to ship an incomplete template that is not easily separated into a standalone module.
|
|
18
22
|
- Modern React Native / Expo apps are **extremely path-sensitive**:
|
|
19
23
|
* file-based routing
|
|
@@ -25,8 +29,6 @@ Only Git's *view* of the filesystem changes.
|
|
|
25
29
|
- submodules don't support interleaving files and folders of template code with custom app code
|
|
26
30
|
- I want to simultaneously develop many apps with a similar architecture
|
|
27
31
|
|
|
28
|
-
## Why Not?
|
|
29
|
-
> If you can super cleanly separate your template from your app or make your sourcecode fully modular, you don't need geet, use a normal repo or maybe submodules.
|
|
30
32
|
|
|
31
33
|
---
|
|
32
34
|
## PreReqs
|
|
@@ -43,9 +45,6 @@ npm install -g geet-geet
|
|
|
43
45
|
geet
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
Try our [Demo](/docs/DEMO.md)
|
|
47
|
-
|
|
48
|
-
---
|
|
49
48
|
|
|
50
49
|
## Table of Contents
|
|
51
50
|
|
|
@@ -58,4 +57,7 @@ Try our [Demo](/docs/DEMO.md)
|
|
|
58
57
|
7. [Advanced: Preventing app-specific code](/docs/PREVENT_COMMIT_PATTERNS.md)
|
|
59
58
|
8. [Contributing to geet](/docs/CONTRIBUTING.md)
|
|
60
59
|
9. [FAQ](/docs/FAQ.md)
|
|
61
|
-
10. [Demo](/docs/DEMO.md)
|
|
60
|
+
10. [Demo](/docs/DEMO.md)
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
package/bin/geet.sh
CHANGED
package/docs/CONTRIBUTING.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Contributing to geet
|
|
2
2
|
|
|
3
3
|
## Development setup
|
|
4
4
|
|
|
@@ -6,13 +6,7 @@
|
|
|
6
6
|
# Clone the repo
|
|
7
7
|
git clone https://github.com/modularizer/geet.git
|
|
8
8
|
cd geet
|
|
9
|
-
|
|
10
|
-
# The scripts are in lib/
|
|
11
|
-
ls lib/
|
|
12
|
-
|
|
13
|
-
# Test any script
|
|
14
|
-
bash lib/doctor.sh help
|
|
15
|
-
bash lib/ghcli.sh help
|
|
9
|
+
npm install -g .
|
|
16
10
|
```
|
|
17
11
|
|
|
18
12
|
## Project structure
|
|
@@ -20,21 +14,33 @@ bash lib/ghcli.sh help
|
|
|
20
14
|
```text
|
|
21
15
|
geet/
|
|
22
16
|
lib/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
17
|
+
detach.sh # File detachment for conflict resolution
|
|
18
|
+
digest-and-locate.sh # Environment setup and routing
|
|
19
|
+
doctor.sh # Health checks
|
|
20
|
+
flags.sh # Flag parsing utilities
|
|
21
|
+
ghcli.sh # GitHub CLI integration
|
|
22
|
+
git.sh # Git operations wrapper
|
|
23
|
+
help.sh # Help text and command listing
|
|
24
|
+
ignored.sh # Check if files are ignored/included/excluded
|
|
25
|
+
include.sh # Manage included files
|
|
26
|
+
init.sh # Initialize layer
|
|
27
|
+
install.sh # Clone and initialize templates
|
|
28
|
+
logger.sh # Logging utilities
|
|
29
|
+
pre-commit # Pre-commit hook
|
|
30
|
+
prework.sh # Show environment info
|
|
31
|
+
session.sh # Isolated build helper
|
|
32
|
+
split.sh # Export layer files
|
|
33
|
+
sync.sh # Compile .geetinclude to .geetexclude
|
|
34
|
+
template.sh # Create new layer
|
|
35
|
+
tree.sh # Inspect layer contents
|
|
36
|
+
version.sh # Version display
|
|
37
|
+
whoops.sh # Open GitHub issues
|
|
38
|
+
why.sh # Show reasons to use/not use geet
|
|
32
39
|
|
|
33
40
|
bin/
|
|
34
|
-
geet.sh
|
|
41
|
+
geet.sh # Main router and npm executable wrapper
|
|
35
42
|
|
|
36
|
-
|
|
37
|
-
package.json # npm package config
|
|
43
|
+
package.json # npm package config
|
|
38
44
|
```
|
|
39
45
|
|
|
40
46
|
## Architecture principles
|
|
@@ -57,16 +63,26 @@ geet/
|
|
|
57
63
|
- Document non-obvious behavior
|
|
58
64
|
- Validate inputs early
|
|
59
65
|
- Fail fast with clear errors
|
|
66
|
+
- Use `source` a lot
|
|
67
|
+
- Use `digest-and-locate.sh` for variable definitions and things that all scripts need access to
|
|
68
|
+
- Use `has_flag`, `extract_flag`, `log`, and `debug` heavily
|
|
60
69
|
|
|
61
70
|
## Testing changes
|
|
62
71
|
|
|
63
72
|
```bash
|
|
64
73
|
# Run doctor to check for issues
|
|
65
|
-
|
|
74
|
+
geet doctor
|
|
75
|
+
|
|
76
|
+
# Test the main executable
|
|
77
|
+
geet help
|
|
78
|
+
geet doctor
|
|
66
79
|
|
|
67
80
|
# Test in a real project
|
|
68
81
|
cd /path/to/test-project
|
|
69
|
-
/path/to/geet/
|
|
82
|
+
/path/to/geet/bin/geet.sh doctor
|
|
83
|
+
|
|
84
|
+
# Or if installed globally
|
|
85
|
+
geet doctor
|
|
70
86
|
```
|
|
71
87
|
|
|
72
88
|
## Common tasks
|
|
@@ -74,8 +90,8 @@ cd /path/to/test-project
|
|
|
74
90
|
### Adding a new command
|
|
75
91
|
|
|
76
92
|
1. Create `lib/mycommand.sh`
|
|
77
|
-
2. Add to `
|
|
78
|
-
3. Update help text
|
|
93
|
+
2. Add to `bin/geet.sh` router
|
|
94
|
+
3. Update help text in `lib/help.sh`
|
|
79
95
|
4. Test thoroughly
|
|
80
96
|
|
|
81
97
|
### Updating documentation
|
|
@@ -127,16 +143,18 @@ This system is intentionally small, explicit, and evolvable.
|
|
|
127
143
|
It will grow **only** when real workflows demand it.
|
|
128
144
|
|
|
129
145
|
Core features:
|
|
130
|
-
- ✅
|
|
131
|
-
- ✅
|
|
146
|
+
- ✅ Template creation and initialization
|
|
147
|
+
- ✅ Install workflow (clone + init)
|
|
132
148
|
- ✅ Layered templates
|
|
133
|
-
- ✅ Include/exclude modes
|
|
134
|
-
- ✅
|
|
135
|
-
- ✅
|
|
149
|
+
- ✅ Include/exclude modes with sync
|
|
150
|
+
- ✅ File detachment (hard and soft)
|
|
151
|
+
- ✅ Introspection tools (tree, prework)
|
|
152
|
+
- ✅ Export functionality (split)
|
|
136
153
|
- ✅ Build sessions
|
|
137
154
|
- ✅ Safety rails
|
|
138
|
-
- ✅
|
|
139
|
-
- ✅ GitHub integration
|
|
155
|
+
- ✅ Pre-commit hooks
|
|
156
|
+
- ✅ GitHub CLI integration (publish, pr, issues)
|
|
140
157
|
- ✅ Multi-layer support
|
|
158
|
+
- ✅ Doctor health checks
|
|
141
159
|
|
|
142
160
|
That's the core. Everything else is optional.
|
package/lib/help.sh
CHANGED
|
@@ -19,6 +19,7 @@ TEMPLATE MANAGEMENT:
|
|
|
19
19
|
FILE MANAGEMENT:
|
|
20
20
|
tree [list|tracked|all] Show what files the template includes
|
|
21
21
|
split <dest> [mode] Export template files to external folder
|
|
22
|
+
inspect <path> Show which layer tracks a file and its git status
|
|
22
23
|
sync Compile .geetinclude whitelist into .geetexclude
|
|
23
24
|
include <path> Manage included files
|
|
24
25
|
ignored|included|excluded <path> Check if a path is ignored/included/excluded
|
|
@@ -61,6 +62,7 @@ USAGE:
|
|
|
61
62
|
install <repo> <dir> [--public|--private|--internal] Do a git clone of a repo and convert it into a repo of your own
|
|
62
63
|
tree [list|tracked|all] Show what files the template includes
|
|
63
64
|
split <dest> [mode] Export template files to external folder
|
|
65
|
+
inspect <path> Show which layer tracks a file and its git status
|
|
64
66
|
prework See what we know
|
|
65
67
|
why / whynot Reasons to (or not to) use geet
|
|
66
68
|
version / --version Show geet version
|
package/lib/inspect.sh
ADDED
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
# inspect.sh — inspect which layer tracks a file and its git status
|
|
2
|
+
# Usage:
|
|
3
|
+
# source inspect.sh
|
|
4
|
+
# inspect <path>
|
|
5
|
+
|
|
6
|
+
inspect() {
|
|
7
|
+
|
|
8
|
+
# digest-and-locate.sh provides: APP_DIR, TEMPLATE_DIR, DOTGIT, TEMPLATE_NAME,
|
|
9
|
+
# GEET_ALIAS, die, log, debug
|
|
10
|
+
|
|
11
|
+
# Color helpers
|
|
12
|
+
_color_enabled() {
|
|
13
|
+
[[ -t 1 ]] || return 1
|
|
14
|
+
case "${COLOR_MODE:-}" in
|
|
15
|
+
light|dark) return 0 ;;
|
|
16
|
+
*) return 1 ;;
|
|
17
|
+
esac
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# ANSI color codes
|
|
21
|
+
if _color_enabled; then
|
|
22
|
+
c_reset=$'\033[0m'
|
|
23
|
+
c_bold=$'\033[1m'
|
|
24
|
+
c_green=$'\033[32m'
|
|
25
|
+
c_red=$'\033[31m'
|
|
26
|
+
c_yellow=$'\033[33m'
|
|
27
|
+
c_blue=$'\033[34m'
|
|
28
|
+
c_cyan=$'\033[36m'
|
|
29
|
+
c_gray=$'\033[90m'
|
|
30
|
+
else
|
|
31
|
+
c_reset=""
|
|
32
|
+
c_bold=""
|
|
33
|
+
c_green=""
|
|
34
|
+
c_red=""
|
|
35
|
+
c_yellow=""
|
|
36
|
+
c_blue=""
|
|
37
|
+
c_cyan=""
|
|
38
|
+
c_gray=""
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Get concise status for a single file
|
|
42
|
+
get_file_status() {
|
|
43
|
+
local file="$1"
|
|
44
|
+
local repo="$2" # "template" or "app"
|
|
45
|
+
|
|
46
|
+
# Check if tracked
|
|
47
|
+
local is_tracked=false
|
|
48
|
+
if [[ "$repo" == "template" ]]; then
|
|
49
|
+
git --git-dir="$DOTGIT" --work-tree="$APP_DIR" ls-files --error-unmatch -- "$file" >/dev/null 2>&1 && is_tracked=true
|
|
50
|
+
else
|
|
51
|
+
git -C "$APP_DIR" ls-files --error-unmatch -- "$file" >/dev/null 2>&1 && is_tracked=true
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
if [[ "$is_tracked" == "false" ]]; then
|
|
55
|
+
# Check if ignored/excluded
|
|
56
|
+
if [[ "$repo" == "template" ]]; then
|
|
57
|
+
if geet_git check-ignore -q -- "$file" 2>/dev/null; then
|
|
58
|
+
echo "excluded"
|
|
59
|
+
return
|
|
60
|
+
fi
|
|
61
|
+
else
|
|
62
|
+
if git -C "$APP_DIR" check-ignore -q -- "$file" 2>/dev/null; then
|
|
63
|
+
echo "ignored"
|
|
64
|
+
return
|
|
65
|
+
fi
|
|
66
|
+
fi
|
|
67
|
+
echo "untracked"
|
|
68
|
+
return
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# File is tracked - check detachment state (template only)
|
|
72
|
+
if [[ "$repo" == "template" ]]; then
|
|
73
|
+
# Check if hard-detached
|
|
74
|
+
local ls_v_output=$(git --git-dir="$DOTGIT" --work-tree="$APP_DIR" ls-files -v -- "$file" 2>/dev/null || echo "")
|
|
75
|
+
if [[ "$ls_v_output" =~ ^S ]]; then
|
|
76
|
+
echo "detached"
|
|
77
|
+
return
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Check if soft-detached
|
|
81
|
+
local merge_attr=$(git --git-dir="$DOTGIT" --work-tree="$APP_DIR" check-attr merge -- "$file" 2>/dev/null | awk -F': ' '{print $3}')
|
|
82
|
+
if [[ "$merge_attr" == "keep-ours" ]]; then
|
|
83
|
+
echo "slid"
|
|
84
|
+
return
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Check if modified
|
|
89
|
+
local status_output=""
|
|
90
|
+
if [[ "$repo" == "template" ]]; then
|
|
91
|
+
status_output=$(git --git-dir="$DOTGIT" --work-tree="$APP_DIR" status --porcelain -- "$file" 2>/dev/null || echo "")
|
|
92
|
+
else
|
|
93
|
+
status_output=$(git -C "$APP_DIR" status --porcelain -- "$file" 2>/dev/null || echo "")
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
if [[ -z "$status_output" ]]; then
|
|
97
|
+
echo "clean"
|
|
98
|
+
else
|
|
99
|
+
local status_code="${status_output:0:2}"
|
|
100
|
+
case "$status_code" in
|
|
101
|
+
" M"|"M "|"MM") echo "modified" ;;
|
|
102
|
+
" D"|"D "|"DD") echo "deleted" ;;
|
|
103
|
+
"A "|"AM") echo "added" ;;
|
|
104
|
+
*) echo "modified" ;;
|
|
105
|
+
esac
|
|
106
|
+
fi
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# Inspect a directory and show summary for all files (recursive)
|
|
110
|
+
inspect_directory() {
|
|
111
|
+
local dir="$1"
|
|
112
|
+
|
|
113
|
+
echo
|
|
114
|
+
echo "${c_bold}Inspecting directory (recursive):${c_reset} $dir"
|
|
115
|
+
echo
|
|
116
|
+
printf "%-60s %-15s %-15s\n" "File" "Template" "App"
|
|
117
|
+
printf "%-60s %-15s %-15s\n" "----" "--------" "---"
|
|
118
|
+
|
|
119
|
+
# Collect files from all three sources: template HEAD, app HEAD, and working tree
|
|
120
|
+
local all_files=$(
|
|
121
|
+
{
|
|
122
|
+
# Files from template repo HEAD
|
|
123
|
+
if [[ -d "$DOTGIT" ]]; then
|
|
124
|
+
geet_git ls-files 2>/dev/null | grep "^${dir}" || true
|
|
125
|
+
fi
|
|
126
|
+
# Files from app repo HEAD
|
|
127
|
+
if [[ -d "$APP_DIR/.git" ]]; then
|
|
128
|
+
git -C "$APP_DIR" ls-files 2>/dev/null | grep "^${dir}" || true
|
|
129
|
+
fi
|
|
130
|
+
# Files from working tree (excluding .git and dot-git directories)
|
|
131
|
+
find "$APP_DIR/$dir" -type d \( -name .git -o -name dot-git \) -prune -o -type f -print 2>/dev/null | sed "s|^$APP_DIR/||; s|^\./||" || true
|
|
132
|
+
} | sort -u
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Process each unique file
|
|
136
|
+
while IFS= read -r rel_path; do
|
|
137
|
+
[[ -z "$rel_path" ]] && continue
|
|
138
|
+
|
|
139
|
+
# Get status for both repos
|
|
140
|
+
local template_status=$(get_file_status "$rel_path" "template")
|
|
141
|
+
local app_status=$(get_file_status "$rel_path" "app")
|
|
142
|
+
|
|
143
|
+
# Color code the status
|
|
144
|
+
local template_colored=""
|
|
145
|
+
local app_colored=""
|
|
146
|
+
|
|
147
|
+
case "$template_status" in
|
|
148
|
+
clean) template_colored="${c_green}clean${c_reset}" ;;
|
|
149
|
+
modified) template_colored="${c_yellow}modified${c_reset}" ;;
|
|
150
|
+
detached) template_colored="${c_yellow}detached${c_reset}" ;;
|
|
151
|
+
slid) template_colored="${c_cyan}slid${c_reset}" ;;
|
|
152
|
+
excluded) template_colored="${c_gray}excluded${c_reset}" ;;
|
|
153
|
+
untracked) template_colored="${c_red}untracked${c_reset}" ;;
|
|
154
|
+
deleted) template_colored="${c_red}deleted${c_reset}" ;;
|
|
155
|
+
*) template_colored="$template_status" ;;
|
|
156
|
+
esac
|
|
157
|
+
|
|
158
|
+
case "$app_status" in
|
|
159
|
+
clean) app_colored="${c_green}clean${c_reset}" ;;
|
|
160
|
+
modified) app_colored="${c_yellow}modified${c_reset}" ;;
|
|
161
|
+
ignored) app_colored="${c_gray}ignored${c_reset}" ;;
|
|
162
|
+
untracked) app_colored="${c_red}untracked${c_reset}" ;;
|
|
163
|
+
deleted) app_colored="${c_red}deleted${c_reset}" ;;
|
|
164
|
+
*) app_colored="$app_status" ;;
|
|
165
|
+
esac
|
|
166
|
+
|
|
167
|
+
# Skip files that are excluded by template AND ignored by app
|
|
168
|
+
[[ "$template_status" == "excluded" && "$app_status" == "ignored" ]] && continue
|
|
169
|
+
|
|
170
|
+
printf "%-60s %-24s %-24s\n" "$rel_path" "$template_colored" "$app_colored"
|
|
171
|
+
done <<< "$all_files"
|
|
172
|
+
|
|
173
|
+
echo
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# Inspect files matching a glob pattern
|
|
177
|
+
inspect_glob() {
|
|
178
|
+
local pattern="$1"
|
|
179
|
+
|
|
180
|
+
echo
|
|
181
|
+
echo "${c_bold}Inspecting pattern:${c_reset} $pattern"
|
|
182
|
+
echo
|
|
183
|
+
printf "%-60s %-15s %-15s\n" "File" "Template" "App"
|
|
184
|
+
printf "%-60s %-15s %-15s\n" "----" "--------" "---"
|
|
185
|
+
|
|
186
|
+
# Collect files from all three sources: template HEAD, app HEAD, and working tree
|
|
187
|
+
local all_files=$(
|
|
188
|
+
{
|
|
189
|
+
# Files from template repo HEAD matching pattern
|
|
190
|
+
if [[ -d "$DOTGIT" ]]; then
|
|
191
|
+
geet_git ls-files 2>/dev/null | while IFS= read -r file; do
|
|
192
|
+
# Use bash pattern matching
|
|
193
|
+
shopt -s globstar nullglob
|
|
194
|
+
case "$file" in
|
|
195
|
+
$pattern) echo "$file" ;;
|
|
196
|
+
esac
|
|
197
|
+
shopt -u globstar nullglob
|
|
198
|
+
done
|
|
199
|
+
fi
|
|
200
|
+
# Files from app repo HEAD matching pattern
|
|
201
|
+
if [[ -d "$APP_DIR/.git" ]]; then
|
|
202
|
+
git -C "$APP_DIR" ls-files 2>/dev/null | while IFS= read -r file; do
|
|
203
|
+
shopt -s globstar nullglob
|
|
204
|
+
case "$file" in
|
|
205
|
+
$pattern) echo "$file" ;;
|
|
206
|
+
esac
|
|
207
|
+
shopt -u globstar nullglob
|
|
208
|
+
done
|
|
209
|
+
fi
|
|
210
|
+
# Files from working tree matching pattern
|
|
211
|
+
shopt -s nullglob globstar
|
|
212
|
+
for f in $APP_DIR/$pattern; do
|
|
213
|
+
if [[ -f "$f" ]]; then
|
|
214
|
+
local path="${f#$APP_DIR/}"
|
|
215
|
+
path="${path#./}"
|
|
216
|
+
echo "$path"
|
|
217
|
+
fi
|
|
218
|
+
done
|
|
219
|
+
shopt -u nullglob globstar
|
|
220
|
+
} | sort -u
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Process each unique file
|
|
224
|
+
while IFS= read -r rel_path; do
|
|
225
|
+
[[ -z "$rel_path" ]] && continue
|
|
226
|
+
|
|
227
|
+
# Get status for both repos
|
|
228
|
+
local template_status=$(get_file_status "$rel_path" "template")
|
|
229
|
+
local app_status=$(get_file_status "$rel_path" "app")
|
|
230
|
+
|
|
231
|
+
# Color code the status
|
|
232
|
+
local template_colored=""
|
|
233
|
+
local app_colored=""
|
|
234
|
+
|
|
235
|
+
case "$template_status" in
|
|
236
|
+
clean) template_colored="${c_green}clean${c_reset}" ;;
|
|
237
|
+
modified) template_colored="${c_yellow}modified${c_reset}" ;;
|
|
238
|
+
detached) template_colored="${c_yellow}detached${c_reset}" ;;
|
|
239
|
+
slid) template_colored="${c_cyan}slid${c_reset}" ;;
|
|
240
|
+
excluded) template_colored="${c_gray}excluded${c_reset}" ;;
|
|
241
|
+
untracked) template_colored="${c_red}untracked${c_reset}" ;;
|
|
242
|
+
deleted) template_colored="${c_red}deleted${c_reset}" ;;
|
|
243
|
+
*) template_colored="$template_status" ;;
|
|
244
|
+
esac
|
|
245
|
+
|
|
246
|
+
case "$app_status" in
|
|
247
|
+
clean) app_colored="${c_green}clean${c_reset}" ;;
|
|
248
|
+
modified) app_colored="${c_yellow}modified${c_reset}" ;;
|
|
249
|
+
ignored) app_colored="${c_gray}ignored${c_reset}" ;;
|
|
250
|
+
untracked) app_colored="${c_red}untracked${c_reset}" ;;
|
|
251
|
+
deleted) app_colored="${c_red}deleted${c_reset}" ;;
|
|
252
|
+
*) app_colored="$app_status" ;;
|
|
253
|
+
esac
|
|
254
|
+
|
|
255
|
+
# Skip files that are excluded by template AND ignored by app
|
|
256
|
+
[[ "$template_status" == "excluded" && "$app_status" == "ignored" ]] && continue
|
|
257
|
+
|
|
258
|
+
printf "%-60s %-24s %-24s\n" "$rel_path" "$template_colored" "$app_colored"
|
|
259
|
+
done <<< "$all_files"
|
|
260
|
+
|
|
261
|
+
echo
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
usage() {
|
|
265
|
+
cat <<EOF
|
|
266
|
+
$GEET_ALIAS inspect — inspect which layer tracks a file and its git status
|
|
267
|
+
|
|
268
|
+
Given a file path, shows detailed inspection with tracking status, commits, and diffs.
|
|
269
|
+
Given a directory path, shows summary table of all files (recursive).
|
|
270
|
+
Given a glob pattern, shows summary table of all matching files.
|
|
271
|
+
|
|
272
|
+
Usage:
|
|
273
|
+
$GEET_ALIAS inspect <path|pattern>
|
|
274
|
+
|
|
275
|
+
Examples:
|
|
276
|
+
$GEET_ALIAS inspect README.md # detailed single file view
|
|
277
|
+
$GEET_ALIAS inspect lib/ # recursive directory summary
|
|
278
|
+
$GEET_ALIAS inspect "**/*.md" # all .md files recursively
|
|
279
|
+
$GEET_ALIAS inspect "lib/*.sh" # glob pattern in lib/
|
|
280
|
+
$GEET_ALIAS inspect . # entire project (recursive)
|
|
281
|
+
$GEET_ALIAS inspect --help
|
|
282
|
+
|
|
283
|
+
Single file output includes:
|
|
284
|
+
- File modification time (mtime)
|
|
285
|
+
- Tracking status (tracked|excluded|ignored|untracked) for both repos
|
|
286
|
+
- Detachment state for template repo (attached|slid|detached)
|
|
287
|
+
- Last commit hash and commit time for each repo
|
|
288
|
+
- Git status (clean|modified|deleted|added)
|
|
289
|
+
- Content comparison across all three states (working tree, template HEAD, app HEAD)
|
|
290
|
+
- Pairwise diffs showing ahead/behind/diverged status
|
|
291
|
+
|
|
292
|
+
Directory/glob output shows one line per file from all three sources:
|
|
293
|
+
- Template HEAD (via geet git ls-files)
|
|
294
|
+
- App HEAD (via git ls-files)
|
|
295
|
+
- Working tree (filesystem)
|
|
296
|
+
|
|
297
|
+
Status indicators:
|
|
298
|
+
- Template: clean|modified|detached|slid|excluded|untracked|deleted
|
|
299
|
+
- App: clean|modified|ignored|untracked|deleted
|
|
300
|
+
|
|
301
|
+
Color coding:
|
|
302
|
+
- Green: clean
|
|
303
|
+
- Yellow: modified, detached
|
|
304
|
+
- Cyan: slid
|
|
305
|
+
- Red: untracked, deleted
|
|
306
|
+
- Gray: excluded, ignored
|
|
307
|
+
|
|
308
|
+
Notes:
|
|
309
|
+
- Directory/glob inspection excludes .git and dot-git directories
|
|
310
|
+
- Files that are excluded by template AND ignored by app are hidden
|
|
311
|
+
- Use quotes around glob patterns to prevent shell expansion
|
|
312
|
+
EOF
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
# Handle help
|
|
316
|
+
if [[ "${1:-}" == "help" || "${1:-}" == "-h" || "${1:-}" == "--help" || -z "${1:-}" ]]; then
|
|
317
|
+
usage
|
|
318
|
+
return 0
|
|
319
|
+
fi
|
|
320
|
+
|
|
321
|
+
path="${1}"
|
|
322
|
+
|
|
323
|
+
# Convert absolute path to relative if needed
|
|
324
|
+
if [[ "$path" == "$APP_DIR/"* ]]; then
|
|
325
|
+
path="${path#"$APP_DIR/"}"
|
|
326
|
+
fi
|
|
327
|
+
|
|
328
|
+
# Strip leading ./ if present
|
|
329
|
+
path="${path#./}"
|
|
330
|
+
|
|
331
|
+
# Check if path is a directory - if so, show summary for all files
|
|
332
|
+
if [[ -d "$APP_DIR/$path" ]]; then
|
|
333
|
+
inspect_directory "$path"
|
|
334
|
+
return 0
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
# Check if path contains glob characters - if so, treat as pattern
|
|
338
|
+
if [[ "$path" == *"*"* || "$path" == *"?"* || "$path" == *"["* ]]; then
|
|
339
|
+
inspect_glob "$path"
|
|
340
|
+
return 0
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
echo
|
|
344
|
+
echo "${c_bold}Path:${c_reset} $path"
|
|
345
|
+
|
|
346
|
+
# Show file mtime if file exists
|
|
347
|
+
if [[ -e "$APP_DIR/$path" ]]; then
|
|
348
|
+
if stat -c '%y' "$APP_DIR/$path" >/dev/null 2>&1; then
|
|
349
|
+
# GNU stat
|
|
350
|
+
file_mtime=$(stat -c '%y' "$APP_DIR/$path" 2>/dev/null | cut -d'.' -f1)
|
|
351
|
+
else
|
|
352
|
+
# BSD stat (macOS)
|
|
353
|
+
file_mtime=$(stat -f '%Sm' -t '%Y-%m-%d %H:%M:%S' "$APP_DIR/$path" 2>/dev/null)
|
|
354
|
+
fi
|
|
355
|
+
[[ -n "$file_mtime" ]] && echo "${c_gray}File modified: $file_mtime${c_reset}"
|
|
356
|
+
fi
|
|
357
|
+
|
|
358
|
+
echo
|
|
359
|
+
|
|
360
|
+
# Template repo inspection
|
|
361
|
+
if [[ -d "$DOTGIT" ]]; then
|
|
362
|
+
template_name="${TEMPLATE_NAME:-template}"
|
|
363
|
+
echo "${c_bold}Template repo${c_reset} ${c_blue}($template_name)${c_reset}:"
|
|
364
|
+
|
|
365
|
+
# Check if tracked in template repo
|
|
366
|
+
is_tracked_template=false
|
|
367
|
+
is_ignored_template=false
|
|
368
|
+
|
|
369
|
+
if git --git-dir="$DOTGIT" --work-tree="$APP_DIR" ls-files --error-unmatch -- "$path" >/dev/null 2>&1; then
|
|
370
|
+
is_tracked_template=true
|
|
371
|
+
fi
|
|
372
|
+
|
|
373
|
+
# Check if ignored by template repo (whitelist system via .geetexclude)
|
|
374
|
+
if geet_git check-ignore -q -- "$path" 2>/dev/null; then
|
|
375
|
+
is_ignored_template=true
|
|
376
|
+
fi
|
|
377
|
+
|
|
378
|
+
# Display tracking status
|
|
379
|
+
if [[ "$is_tracked_template" == "true" ]]; then
|
|
380
|
+
echo " ${c_green}✔ tracked${c_reset}"
|
|
381
|
+
|
|
382
|
+
# Determine detachment state
|
|
383
|
+
# Check if hard-detached (skip-worktree)
|
|
384
|
+
ls_v_output=$(git --git-dir="$DOTGIT" --work-tree="$APP_DIR" ls-files -v -- "$path" 2>/dev/null || echo "")
|
|
385
|
+
if [[ "$ls_v_output" =~ ^S ]]; then
|
|
386
|
+
echo " ${c_yellow}state: detached${c_reset}"
|
|
387
|
+
else
|
|
388
|
+
# Check if soft-detached (merge=keep-ours)
|
|
389
|
+
merge_attr=$(git --git-dir="$DOTGIT" --work-tree="$APP_DIR" check-attr merge -- "$path" 2>/dev/null | awk -F': ' '{print $3}')
|
|
390
|
+
if [[ "$merge_attr" == "keep-ours" ]]; then
|
|
391
|
+
echo " ${c_cyan}state: slid${c_reset}"
|
|
392
|
+
else
|
|
393
|
+
echo " ${c_green}state: attached${c_reset}"
|
|
394
|
+
fi
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
# Get the commit hash and time for this file (last commit that touched it)
|
|
398
|
+
commit_hash=$(git --git-dir="$DOTGIT" --work-tree="$APP_DIR" log -1 --format="%h" -- "$path" 2>/dev/null || echo "")
|
|
399
|
+
if [[ -n "$commit_hash" ]]; then
|
|
400
|
+
echo " ${c_gray}commit: $commit_hash${c_reset}"
|
|
401
|
+
|
|
402
|
+
# Get commit time
|
|
403
|
+
commit_time=$(git --git-dir="$DOTGIT" --work-tree="$APP_DIR" log -1 --format="%ci" -- "$path" 2>/dev/null | cut -d'.' -f1 || echo "")
|
|
404
|
+
[[ -n "$commit_time" ]] && echo " ${c_gray}commit time: $commit_time${c_reset}"
|
|
405
|
+
fi
|
|
406
|
+
|
|
407
|
+
# Check status (modified, deleted, etc.)
|
|
408
|
+
status_output=$(git --git-dir="$DOTGIT" --work-tree="$APP_DIR" status --porcelain -- "$path" 2>/dev/null || echo "")
|
|
409
|
+
|
|
410
|
+
if [[ -z "$status_output" ]]; then
|
|
411
|
+
echo " ${c_green}status: clean${c_reset}"
|
|
412
|
+
else
|
|
413
|
+
# Parse status code
|
|
414
|
+
status_code="${status_output:0:2}"
|
|
415
|
+
case "$status_code" in
|
|
416
|
+
" M"|"M "|"MM") echo " ${c_yellow}status: modified${c_reset}" ;;
|
|
417
|
+
" D"|"D "|"DD") echo " ${c_red}status: deleted${c_reset}" ;;
|
|
418
|
+
"A "|"AM") echo " ${c_cyan}status: added${c_reset}" ;;
|
|
419
|
+
"??") echo " ${c_gray}status: untracked${c_reset}" ;;
|
|
420
|
+
*) echo " status: ${status_code}" ;;
|
|
421
|
+
esac
|
|
422
|
+
fi
|
|
423
|
+
|
|
424
|
+
template_tracked=true
|
|
425
|
+
else
|
|
426
|
+
# Not tracked - check if excluded or just untracked
|
|
427
|
+
if [[ "$is_ignored_template" == "true" ]]; then
|
|
428
|
+
echo " ${c_yellow}✖ excluded${c_reset}"
|
|
429
|
+
else
|
|
430
|
+
echo " ${c_red}✖ untracked${c_reset}"
|
|
431
|
+
fi
|
|
432
|
+
template_tracked=false
|
|
433
|
+
fi
|
|
434
|
+
else
|
|
435
|
+
echo "${c_bold}Template repo:${c_reset}"
|
|
436
|
+
echo " ${c_red}✖ not initialized${c_reset}"
|
|
437
|
+
template_tracked=false
|
|
438
|
+
fi
|
|
439
|
+
|
|
440
|
+
echo
|
|
441
|
+
|
|
442
|
+
# App repo inspection
|
|
443
|
+
# Find app repo .git directory
|
|
444
|
+
if [[ -d "$APP_DIR/.git" ]]; then
|
|
445
|
+
app_name="${APP_NAME:-app}"
|
|
446
|
+
echo "${c_bold}App repo${c_reset} ${c_blue}($app_name)${c_reset}:"
|
|
447
|
+
|
|
448
|
+
# Check if tracked in app repo
|
|
449
|
+
is_tracked_app=false
|
|
450
|
+
is_ignored_app=false
|
|
451
|
+
|
|
452
|
+
if git -C "$APP_DIR" ls-files --error-unmatch -- "$path" >/dev/null 2>&1; then
|
|
453
|
+
is_tracked_app=true
|
|
454
|
+
fi
|
|
455
|
+
|
|
456
|
+
# Check if ignored by app repo
|
|
457
|
+
if git -C "$APP_DIR" check-ignore -q -- "$path" 2>/dev/null; then
|
|
458
|
+
is_ignored_app=true
|
|
459
|
+
fi
|
|
460
|
+
|
|
461
|
+
# Display tracking status
|
|
462
|
+
if [[ "$is_tracked_app" == "true" ]]; then
|
|
463
|
+
echo " ${c_green}✔ tracked${c_reset}"
|
|
464
|
+
|
|
465
|
+
# Get the commit hash and time for this file (last commit that touched it)
|
|
466
|
+
commit_hash=$(git -C "$APP_DIR" log -1 --format="%h" -- "$path" 2>/dev/null || echo "")
|
|
467
|
+
if [[ -n "$commit_hash" ]]; then
|
|
468
|
+
echo " ${c_gray}commit: $commit_hash${c_reset}"
|
|
469
|
+
|
|
470
|
+
# Get commit time
|
|
471
|
+
commit_time=$(git -C "$APP_DIR" log -1 --format="%ci" -- "$path" 2>/dev/null | cut -d'.' -f1 || echo "")
|
|
472
|
+
[[ -n "$commit_time" ]] && echo " ${c_gray}commit time: $commit_time${c_reset}"
|
|
473
|
+
fi
|
|
474
|
+
|
|
475
|
+
# Check status
|
|
476
|
+
status_output=$(git -C "$APP_DIR" status --porcelain -- "$path" 2>/dev/null || echo "")
|
|
477
|
+
|
|
478
|
+
if [[ -z "$status_output" ]]; then
|
|
479
|
+
echo " ${c_green}status: clean${c_reset}"
|
|
480
|
+
else
|
|
481
|
+
# Parse status code
|
|
482
|
+
status_code="${status_output:0:2}"
|
|
483
|
+
case "$status_code" in
|
|
484
|
+
" M"|"M "|"MM") echo " ${c_yellow}status: modified${c_reset}" ;;
|
|
485
|
+
" D"|"D "|"DD") echo " ${c_red}status: deleted${c_reset}" ;;
|
|
486
|
+
"A "|"AM") echo " ${c_cyan}status: added${c_reset}" ;;
|
|
487
|
+
"??") echo " ${c_gray}status: untracked${c_reset}" ;;
|
|
488
|
+
*) echo " status: ${status_code}" ;;
|
|
489
|
+
esac
|
|
490
|
+
fi
|
|
491
|
+
|
|
492
|
+
app_tracked=true
|
|
493
|
+
else
|
|
494
|
+
# Not tracked - check if ignored or just untracked
|
|
495
|
+
if [[ "$is_ignored_app" == "true" ]]; then
|
|
496
|
+
echo " ${c_yellow}✖ ignored${c_reset}"
|
|
497
|
+
else
|
|
498
|
+
echo " ${c_red}✖ untracked${c_reset}"
|
|
499
|
+
fi
|
|
500
|
+
app_tracked=false
|
|
501
|
+
fi
|
|
502
|
+
else
|
|
503
|
+
echo "${c_bold}App repo:${c_reset}"
|
|
504
|
+
echo " ${c_red}✖ not found${c_reset}"
|
|
505
|
+
app_tracked=false
|
|
506
|
+
fi
|
|
507
|
+
|
|
508
|
+
echo
|
|
509
|
+
|
|
510
|
+
# Content comparison across all three states
|
|
511
|
+
if [[ -e "$APP_DIR/$path" ]] || [[ "$template_tracked" == "true" ]] || [[ "$app_tracked" == "true" ]]; then
|
|
512
|
+
echo "${c_bold}Content comparison:${c_reset}"
|
|
513
|
+
|
|
514
|
+
# Get checksums for comparison
|
|
515
|
+
working_hash=""
|
|
516
|
+
template_head_hash=""
|
|
517
|
+
app_head_hash=""
|
|
518
|
+
|
|
519
|
+
# Working tree hash
|
|
520
|
+
if [[ -e "$APP_DIR/$path" ]]; then
|
|
521
|
+
working_hash=$(md5sum "$APP_DIR/$path" 2>/dev/null | cut -d' ' -f1 || echo "")
|
|
522
|
+
fi
|
|
523
|
+
|
|
524
|
+
# Template HEAD hash
|
|
525
|
+
if [[ "$template_tracked" == "true" ]]; then
|
|
526
|
+
template_head_hash=$(git --git-dir="$DOTGIT" --work-tree="$APP_DIR" show HEAD:"$path" 2>/dev/null | md5sum | cut -d' ' -f1 || echo "")
|
|
527
|
+
fi
|
|
528
|
+
|
|
529
|
+
# App HEAD hash
|
|
530
|
+
if [[ "$app_tracked" == "true" ]]; then
|
|
531
|
+
app_head_hash=$(git -C "$APP_DIR" show HEAD:"$path" 2>/dev/null | md5sum | cut -d' ' -f1 || echo "")
|
|
532
|
+
fi
|
|
533
|
+
|
|
534
|
+
# Compare and show relationships
|
|
535
|
+
working_exists=$([[ -n "$working_hash" ]] && echo "true" || echo "false")
|
|
536
|
+
template_exists=$([[ -n "$template_head_hash" ]] && echo "true" || echo "false")
|
|
537
|
+
app_exists=$([[ -n "$app_head_hash" ]] && echo "true" || echo "false")
|
|
538
|
+
|
|
539
|
+
# Build comparison display
|
|
540
|
+
if [[ "$working_exists" == "true" && "$template_exists" == "true" && "$app_exists" == "true" ]]; then
|
|
541
|
+
# All three exist - compare them
|
|
542
|
+
if [[ "$working_hash" == "$template_head_hash" && "$working_hash" == "$app_head_hash" ]]; then
|
|
543
|
+
echo " ${c_green}✓ All three states identical${c_reset}"
|
|
544
|
+
elif [[ "$working_hash" == "$template_head_hash" && "$working_hash" != "$app_head_hash" ]]; then
|
|
545
|
+
echo " ${c_yellow}Working tree = Template HEAD ≠ App HEAD${c_reset}"
|
|
546
|
+
elif [[ "$working_hash" == "$app_head_hash" && "$working_hash" != "$template_head_hash" ]]; then
|
|
547
|
+
echo " ${c_yellow}Working tree = App HEAD ≠ Template HEAD${c_reset}"
|
|
548
|
+
elif [[ "$template_head_hash" == "$app_head_hash" && "$working_hash" != "$template_head_hash" ]]; then
|
|
549
|
+
echo " ${c_yellow}Template HEAD = App HEAD ≠ Working tree${c_reset}"
|
|
550
|
+
else
|
|
551
|
+
echo " ${c_red}✗ All three states differ${c_reset}"
|
|
552
|
+
fi
|
|
553
|
+
elif [[ "$working_exists" == "true" && "$template_exists" == "true" && "$app_exists" == "false" ]]; then
|
|
554
|
+
if [[ "$working_hash" == "$template_head_hash" ]]; then
|
|
555
|
+
echo " ${c_green}Working tree = Template HEAD${c_reset} ${c_gray}(not in app HEAD)${c_reset}"
|
|
556
|
+
else
|
|
557
|
+
echo " ${c_yellow}Working tree ≠ Template HEAD${c_reset} ${c_gray}(not in app HEAD)${c_reset}"
|
|
558
|
+
fi
|
|
559
|
+
elif [[ "$working_exists" == "true" && "$template_exists" == "false" && "$app_exists" == "true" ]]; then
|
|
560
|
+
if [[ "$working_hash" == "$app_head_hash" ]]; then
|
|
561
|
+
echo " ${c_green}Working tree = App HEAD${c_reset} ${c_gray}(not in template HEAD)${c_reset}"
|
|
562
|
+
else
|
|
563
|
+
echo " ${c_yellow}Working tree ≠ App HEAD${c_reset} ${c_gray}(not in template HEAD)${c_reset}"
|
|
564
|
+
fi
|
|
565
|
+
elif [[ "$working_exists" == "false" && "$template_exists" == "true" && "$app_exists" == "true" ]]; then
|
|
566
|
+
if [[ "$template_head_hash" == "$app_head_hash" ]]; then
|
|
567
|
+
echo " ${c_green}Template HEAD = App HEAD${c_reset} ${c_gray}(no working tree)${c_reset}"
|
|
568
|
+
else
|
|
569
|
+
echo " ${c_yellow}Template HEAD ≠ App HEAD${c_reset} ${c_gray}(no working tree)${c_reset}"
|
|
570
|
+
fi
|
|
571
|
+
fi
|
|
572
|
+
|
|
573
|
+
echo
|
|
574
|
+
fi
|
|
575
|
+
|
|
576
|
+
# Show all three pairwise diffs
|
|
577
|
+
echo "${c_bold}Diffs:${c_reset}"
|
|
578
|
+
|
|
579
|
+
# Create temp files for all three states
|
|
580
|
+
tmp_working=$(mktemp)
|
|
581
|
+
tmp_template_head=$(mktemp)
|
|
582
|
+
tmp_app_head=$(mktemp)
|
|
583
|
+
|
|
584
|
+
# Get contents
|
|
585
|
+
working_file_exists=false
|
|
586
|
+
template_head_exists=false
|
|
587
|
+
app_head_exists=false
|
|
588
|
+
|
|
589
|
+
if [[ -e "$APP_DIR/$path" ]]; then
|
|
590
|
+
cp "$APP_DIR/$path" "$tmp_working" 2>/dev/null && working_file_exists=true
|
|
591
|
+
fi
|
|
592
|
+
|
|
593
|
+
if [[ "$template_tracked" == "true" ]]; then
|
|
594
|
+
git --git-dir="$DOTGIT" --work-tree="$APP_DIR" show HEAD:"$path" > "$tmp_template_head" 2>/dev/null && template_head_exists=true
|
|
595
|
+
fi
|
|
596
|
+
|
|
597
|
+
if [[ "$app_tracked" == "true" ]]; then
|
|
598
|
+
git -C "$APP_DIR" show HEAD:"$path" > "$tmp_app_head" 2>/dev/null && app_head_exists=true
|
|
599
|
+
fi
|
|
600
|
+
|
|
601
|
+
# Helper function to compute diff stats on one line
|
|
602
|
+
compute_diff() {
|
|
603
|
+
local file1="$1"
|
|
604
|
+
local file2="$2"
|
|
605
|
+
local name1="$3"
|
|
606
|
+
local name2="$4"
|
|
607
|
+
local show_status="${5:-false}" # Show ahead/behind for HEAD comparisons
|
|
608
|
+
|
|
609
|
+
if cmp -s "$file1" "$file2"; then
|
|
610
|
+
echo " ${c_green}${name1} → ${name2}: identical${c_reset}"
|
|
611
|
+
else
|
|
612
|
+
local tmp_d=$(mktemp)
|
|
613
|
+
diff -u "$file1" "$file2" > "$tmp_d" 2>/dev/null || true
|
|
614
|
+
set +o pipefail
|
|
615
|
+
local add=$(grep "^\+" "$tmp_d" | grep -v "^\+\+\+" | wc -l | tr -d ' \n\r')
|
|
616
|
+
local rem=$(grep "^\-" "$tmp_d" | grep -v "^\-\-\-" | wc -l | tr -d ' \n\r')
|
|
617
|
+
set -o pipefail
|
|
618
|
+
add=${add:-0}
|
|
619
|
+
rem=${rem:-0}
|
|
620
|
+
rm -f "$tmp_d"
|
|
621
|
+
|
|
622
|
+
# Build descriptive diff summary
|
|
623
|
+
local summary=""
|
|
624
|
+
local status=""
|
|
625
|
+
|
|
626
|
+
if [[ "$add" -eq 0 && "$rem" -gt 0 ]]; then
|
|
627
|
+
summary="${name2} has ${rem} fewer line(s)"
|
|
628
|
+
[[ "$show_status" == "true" ]] && status=" ${c_cyan}(${name2} is behind ${name1})${c_reset}"
|
|
629
|
+
elif [[ "$add" -gt 0 && "$rem" -eq 0 ]]; then
|
|
630
|
+
summary="${name2} has ${add} more line(s)"
|
|
631
|
+
[[ "$show_status" == "true" ]] && status=" ${c_cyan}(${name2} is ahead ${name1})${c_reset}"
|
|
632
|
+
elif [[ "$add" -gt 0 && "$rem" -gt 0 ]]; then
|
|
633
|
+
summary="${name2} has ${add} more, ${rem} fewer lines"
|
|
634
|
+
status=" ${c_red}(diverged)${c_reset}"
|
|
635
|
+
fi
|
|
636
|
+
|
|
637
|
+
echo " ${c_yellow}${name1} → ${name2}:${c_reset} ${summary}${status}"
|
|
638
|
+
fi
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
# Diff 1: Working tree vs Template HEAD
|
|
642
|
+
if [[ "$working_file_exists" == "true" && "$template_head_exists" == "true" ]]; then
|
|
643
|
+
compute_diff "$tmp_working" "$tmp_template_head" "Working tree" "Template HEAD" "true"
|
|
644
|
+
fi
|
|
645
|
+
|
|
646
|
+
# Diff 2: Working tree vs App HEAD
|
|
647
|
+
if [[ "$working_file_exists" == "true" && "$app_head_exists" == "true" ]]; then
|
|
648
|
+
compute_diff "$tmp_working" "$tmp_app_head" "Working tree" "App HEAD" "true"
|
|
649
|
+
fi
|
|
650
|
+
|
|
651
|
+
# Diff 3: Template HEAD vs App HEAD (show ahead/behind status)
|
|
652
|
+
if [[ "$template_head_exists" == "true" && "$app_head_exists" == "true" ]]; then
|
|
653
|
+
compute_diff "$tmp_template_head" "$tmp_app_head" "Template HEAD" "App HEAD" "true"
|
|
654
|
+
fi
|
|
655
|
+
|
|
656
|
+
# Cleanup
|
|
657
|
+
rm -f "$tmp_working" "$tmp_template_head" "$tmp_app_head"
|
|
658
|
+
echo
|
|
659
|
+
|
|
660
|
+
} # end of inspect()
|
package/lib/version.sh
CHANGED
|
File without changes
|
package/lib/why.sh
CHANGED
|
@@ -5,23 +5,33 @@
|
|
|
5
5
|
# whynot
|
|
6
6
|
|
|
7
7
|
why() {
|
|
8
|
-
cat <<'EOF'
|
|
9
|
-
|
|
8
|
+
printf '%b' "$(cat <<'EOF'
|
|
9
|
+
Use geet if this resonates with you...
|
|
10
|
+
\033[3m
|
|
11
|
+
"I built something useful, and I think that SOME but not all of my code is re-usable.
|
|
12
|
+
I want to publish some of my code for others to use (or to re-use myself)...
|
|
13
|
+
but I don't want to spend weeks refactoring to split apart the reusable code from the implementation-specific code.
|
|
14
|
+
In fact, it may not even be possible to move around all my files without breaking things.
|
|
15
|
+
Plus, supporting this template is my secondary task which I want to do in tandem with my primary development,
|
|
16
|
+
using my main repository's working directory and publishing some pieces to the template repo."
|
|
17
|
+
|
|
18
|
+
\033[0m
|
|
10
19
|
|
|
11
|
-
I built something useful, and I think that SOME but not all of my code is re-usable.
|
|
12
|
-
I want to publish some of my code for other's to use (or to re-use myself)...
|
|
13
|
-
but I don't want to spend weeks refactoring to split apart the reusable code from the implementation-specific code.
|
|
14
|
-
In fact, it may not even be possible to move around all my files without breaking things.
|
|
15
|
-
Plus, supporting this template is my secondary task which I want to do in tandem with my primary development,
|
|
16
|
-
using my main repository's working directory and publishing some pieces to the template repo.
|
|
17
20
|
EOF
|
|
21
|
+
)"
|
|
22
|
+
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
whynot() {
|
|
21
|
-
cat <<'EOF'
|
|
22
|
-
|
|
26
|
+
printf '%b' "$(cat <<'EOF'
|
|
27
|
+
As much as I love this package, it is not for every use case...
|
|
28
|
+
\033[3m
|
|
29
|
+
You probably do not need geet if you can super cleanly separate your template from your app or make your source code fully modular.
|
|
30
|
+
If you can use a package distribution like pypi or npm that is probably best. If git submodules work for you, that is good too.
|
|
31
|
+
If those solutions are not working for you, then check back here.
|
|
32
|
+
|
|
33
|
+
\033[0m
|
|
23
34
|
|
|
24
|
-
If you can super cleanly separate your template from your app or make your sourcecode fully modular,
|
|
25
|
-
you don't need geet, use a normal repo or maybe submodules.
|
|
26
35
|
EOF
|
|
36
|
+
)"
|
|
27
37
|
}
|