@kova1/pullr 0.4.0 → 0.6.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/README.md +40 -16
- package/bin/pullr +117 -78
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://github.com/kova1max/homebrew-tap)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Safe, fast-forward pull every git repository under a directory.
|
|
9
9
|
|
|
10
10
|
Built for workspaces made of many independent repositories, where keeping
|
|
11
11
|
everything current means `cd`-ing into each one.
|
|
@@ -14,11 +14,13 @@ everything current means `cd`-ing into each one.
|
|
|
14
14
|
$ pullr ~/work
|
|
15
15
|
+ api (main, 3 new commits)
|
|
16
16
|
= web (main)
|
|
17
|
+
~ docs (main, 2 commits held back by local changes)
|
|
17
18
|
- scratch (main has no upstream)
|
|
18
19
|
x infra (main)
|
|
19
|
-
|
|
20
|
+
diverged from upstream (behind 2, ahead 1): cannot fast-forward
|
|
20
21
|
|
|
21
|
-
Repos:
|
|
22
|
+
Repos: 5 updated: 1 up to date: 1 dirty: 1 skipped: 1 failed: 1
|
|
23
|
+
Dirty: docs
|
|
22
24
|
Failed: infra
|
|
23
25
|
```
|
|
24
26
|
|
|
@@ -38,9 +40,6 @@ To try it without installing anything:
|
|
|
38
40
|
npx @kova1/pullr --dry-run ~/work
|
|
39
41
|
```
|
|
40
42
|
|
|
41
|
-
Or copy [`bin/pullr`](bin/pullr) anywhere on your `PATH` - it is a single bash
|
|
42
|
-
script with no dependencies beyond `git`.
|
|
43
|
-
|
|
44
43
|
Every release is also mirrored to
|
|
45
44
|
[GitHub Packages](https://github.com/kova1max/pullr/pkgs/npm/pullr) as
|
|
46
45
|
`@kova1max/pullr` (GitHub requires the repo owner as scope). Installing from
|
|
@@ -56,8 +55,10 @@ first. It will never:
|
|
|
56
55
|
failed and left exactly as it was. Rebasing it is opt-in with `--rebase`,
|
|
57
56
|
and a rebase that hits a conflict is aborted, leaving the repository as it
|
|
58
57
|
was.
|
|
59
|
-
- **Stash or discard your changes.** There is no auto-stash
|
|
60
|
-
|
|
58
|
+
- **Stash, overwrite or discard your changes.** There is no auto-stash. If
|
|
59
|
+
local edits or untracked files are in the way of incoming changes, the
|
|
60
|
+
repository is reported as dirty and left as it was; edits that don't touch
|
|
61
|
+
incoming files don't stop the update.
|
|
61
62
|
- **Touch a repository on a detached `HEAD`**, or a branch with no upstream.
|
|
62
63
|
Both are skipped.
|
|
63
64
|
- **Enter a nested repository.** Repositories inside another repository,
|
|
@@ -70,10 +71,10 @@ first. It will never:
|
|
|
70
71
|
pullr [options] [DIR]
|
|
71
72
|
```
|
|
72
73
|
|
|
73
|
-
| Option | Default | Meaning |
|
|
74
|
-
|
|
|
75
|
-
| `DIR` | current
|
|
76
|
-
| `-j`, `--jobs N` | `
|
|
74
|
+
| Option | Default | Meaning |
|
|
75
|
+
| :--- | :--- | :--- |
|
|
76
|
+
| `DIR` | current directory | Where to look for repositories |
|
|
77
|
+
| `-j`, `--jobs N` | `8` | Work on up to `N` repositories in parallel. Output is buffered per repository and printed in discovery order, so it reads the same as a sequential run. With `N > 1` git never prompts for credentials; use `--jobs 1` for repositories that need an interactive login. |
|
|
77
78
|
| `--max-depth N` | `2` | How many directory levels below `DIR` to search. `0` = only `DIR` itself, `1` = `DIR` and its direct children, ... |
|
|
78
79
|
| `-n`, `--dry-run` | | Show what a run would do without pulling, see below |
|
|
79
80
|
| `-r`, `--rebase` | off | Rebase local commits onto the upstream instead of refusing a diverged branch (`git pull --rebase`). A rebase that hits a conflict is aborted and the repository is left as it was. |
|
|
@@ -83,9 +84,11 @@ pullr [options] [DIR]
|
|
|
83
84
|
|
|
84
85
|
### Output
|
|
85
86
|
|
|
86
|
-
Each repository gets one line: `+` updated, `=` already up to date,
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
Each repository gets one line: `+` updated, `=` already up to date, `~`
|
|
88
|
+
dirty (local changes are in the way of the update, which is held back), `-`
|
|
89
|
+
skipped, `x` failed (with the reason indented below it). Exits `1` if any
|
|
90
|
+
pull failed, `2` on invalid arguments, `0` otherwise; a dirty repository is
|
|
91
|
+
not a failure.
|
|
89
92
|
|
|
90
93
|
### Dry run
|
|
91
94
|
|
|
@@ -100,7 +103,7 @@ x infra (main)
|
|
|
100
103
|
diverged from upstream (behind 2, ahead 1): cannot fast-forward
|
|
101
104
|
- scratch (main has no upstream)
|
|
102
105
|
|
|
103
|
-
Repos: 4 would update: 1 up to date: 1 skipped: 1 would fail: 1
|
|
106
|
+
Repos: 4 would update: 1 up to date: 1 dirty: 0 skipped: 1 would fail: 1
|
|
104
107
|
Failed: infra
|
|
105
108
|
```
|
|
106
109
|
|
|
@@ -111,6 +114,27 @@ remote as it is now, then prints the same lines a real run would (with
|
|
|
111
114
|
only updates remote-tracking refs: the working tree and current branch are
|
|
112
115
|
never modified. Exit codes match a real run.
|
|
113
116
|
|
|
117
|
+
## How it compares
|
|
118
|
+
|
|
119
|
+
| | pullr | [gita](https://github.com/nosarthur/gita) | [gitup](https://github.com/earwig/git-repo-updater) | [myrepos](https://myrepos.branchable.com) (`mr`) | [mani](https://github.com/alajmo/mani) |
|
|
120
|
+
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
121
|
+
| Needs | bash and git | Python 3.8+ | Python 3.10+ | Perl | Go binary |
|
|
122
|
+
| Finds repos | scans a directory | register first (`gita add`) | scans a directory | register first (`~/.mrconfig`) | register first (`mani.yaml`) |
|
|
123
|
+
| Pulls in parallel | 8 at a time by default | all at once, no limit | no | opt-in | opt-in |
|
|
124
|
+
| Update | fetch + fast‑forward | `git pull` | fetch + fast‑forward | `git pull` | no built-in pull |
|
|
125
|
+
| Merge commits | never | depends on your git config | never | depends on your git config | depends on your command |
|
|
126
|
+
| Local changes in the way | reported as dirty | git's error | skipped | git's error | - |
|
|
127
|
+
| Dry run | yes | no | no | no | prints commands |
|
|
128
|
+
|
|
129
|
+
**Why "8 at a time" and not "all at once".** Many self-hosted git servers
|
|
130
|
+
limit how many SSH connections can be opening at the same time; OpenSSH's
|
|
131
|
+
default (`MaxStartups 10:30:100`) starts refusing them past 10. Fetching
|
|
132
|
+
every repository at once then fails for reasons that have nothing to do with
|
|
133
|
+
your repositories. On a self-hosted GitLab with 61 repositories, 16 parallel
|
|
134
|
+
fetches produced 6-9 of those spurious failures and 32 produced 10-14, while
|
|
135
|
+
8 produced none. GitHub itself handled 64 at once without errors, so raise
|
|
136
|
+
`-j` freely there.
|
|
137
|
+
|
|
114
138
|
## License
|
|
115
139
|
|
|
116
140
|
[MIT](LICENSE)
|
package/bin/pullr
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Usage: pullr [--jobs N] [--max-depth N] [--dry-run] [--rebase] [--no-color] [DIR]
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
|
-
VERSION="0.
|
|
6
|
+
VERSION="0.6.0" # set by the release workflow
|
|
7
7
|
|
|
8
8
|
usage() {
|
|
9
9
|
cat <<'EOF'
|
|
@@ -13,7 +13,7 @@ Finds git repositories under DIR (default: current directory) and runs
|
|
|
13
13
|
`git pull --ff-only` in each. Does not descend into a repository once found.
|
|
14
14
|
|
|
15
15
|
Options:
|
|
16
|
-
-j, --jobs N Work on up to N repositories in parallel (default:
|
|
16
|
+
-j, --jobs N Work on up to N repositories in parallel (default: 8).
|
|
17
17
|
Output is buffered per repository and printed in order.
|
|
18
18
|
With N > 1 git never prompts for credentials.
|
|
19
19
|
--max-depth N How many directory levels below DIR to search (default: 2).
|
|
@@ -32,7 +32,7 @@ EOF
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
mode=pull
|
|
35
|
-
jobs=
|
|
35
|
+
jobs=8
|
|
36
36
|
rebase=0
|
|
37
37
|
color=1
|
|
38
38
|
max_depth=2
|
|
@@ -84,96 +84,133 @@ indented() {
|
|
|
84
84
|
printf ' %s\n' "${1//$'\n'/$'\n' }"
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
# Shared first half of a pull and a dry run: check the branch, fetch, and
|
|
88
|
+
# count commits. On success it sets the caller's branch, behind and ahead;
|
|
89
|
+
# otherwise it prints the result line and returns 1.
|
|
90
|
+
prepare() {
|
|
91
|
+
local repo="$1" rel="$2" out counts
|
|
91
92
|
if ! branch="$(git -C "$repo" symbolic-ref --quiet --short HEAD)"; then
|
|
92
93
|
echo skipped
|
|
93
94
|
printf '%s-%s %s %s(detached HEAD)%s\n' "$yellow" "$reset" "$rel" "$dim" "$reset"
|
|
94
|
-
return
|
|
95
|
+
return 1
|
|
95
96
|
fi
|
|
96
97
|
if ! git -C "$repo" rev-parse --quiet --verify '@{upstream}' >/dev/null 2>&1; then
|
|
97
98
|
echo skipped
|
|
98
99
|
printf '%s-%s %s %s(%s has no upstream)%s\n' "$yellow" "$reset" "$rel" "$dim" "$branch" "$reset"
|
|
99
|
-
return
|
|
100
|
+
return 1
|
|
100
101
|
fi
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
before="$(git -C "$repo" rev-parse HEAD)"
|
|
105
|
-
if ! out="$(git -C "$repo" pull "$how" 2>&1)"; then
|
|
106
|
-
# a conflicting rebase must not leave the repository mid-rebase
|
|
107
|
-
[[ "$rebase" == 0 ]] || git -C "$repo" rebase --abort >/dev/null 2>&1 || true
|
|
108
|
-
printf 'failed\t%s\n' "$rel"
|
|
109
|
-
printf '%sx%s %s %s(%s)%s\n' "$red" "$reset" "$rel" "$dim" "$branch" "$reset"
|
|
110
|
-
indented "$out"
|
|
111
|
-
return
|
|
102
|
+
if ! out="$(git -C "$repo" fetch --quiet 2>&1)"; then
|
|
103
|
+
report_failed "$rel" "$branch" "$out"
|
|
104
|
+
return 1
|
|
112
105
|
fi
|
|
113
|
-
|
|
106
|
+
counts="$(git -C "$repo" rev-list --left-right --count '@{upstream}...HEAD')"
|
|
107
|
+
behind="${counts%%[[:space:]]*}"
|
|
108
|
+
ahead="${counts##*[[:space:]]}"
|
|
109
|
+
}
|
|
114
110
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
111
|
+
report_failed() {
|
|
112
|
+
printf 'failed\t%s\n' "$1"
|
|
113
|
+
printf '%sx%s %s %s(%s)%s\n' "$red" "$reset" "$1" "$dim" "$2" "$reset"
|
|
114
|
+
indented "$3"
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
report_diverged() {
|
|
118
|
+
report_failed "$1" "$2" "diverged from upstream (behind $3, ahead $4): cannot fast-forward"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
report_dirty() {
|
|
122
|
+
printf 'dirty\t%s\n' "$1"
|
|
123
|
+
printf '%s~%s %s %s(%s, %s held back by local changes)%s\n' \
|
|
124
|
+
"$yellow" "$reset" "$1" "$dim" "$2" "$(commits "$3")" "$reset"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
report_current() {
|
|
128
|
+
echo current
|
|
129
|
+
printf '%s=%s %s %s(%s)%s\n' "$dim" "$reset" "$1" "$dim" "$2" "$reset"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
report_updated() {
|
|
133
|
+
local note
|
|
134
|
+
note="$3 new commit$([[ "$3" == 1 ]] || printf s)"
|
|
135
|
+
[[ "$4" == 0 ]] || note+=", $4 $5"
|
|
124
136
|
echo updated
|
|
125
|
-
printf '%s+%s %s %s(%s, %s)%s\n' "$green" "$reset" "$
|
|
126
|
-
"$(update_note "$n" "$local_n" rebased)" "$reset"
|
|
137
|
+
printf '%s+%s %s %s(%s, %s)%s\n' "$green" "$reset" "$1" "$dim" "$2" "$note" "$reset"
|
|
127
138
|
}
|
|
128
139
|
|
|
129
|
-
# "
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
note="$n new commit$([[ "$n" == 1 ]] || printf s)"
|
|
133
|
-
[[ "$local_n" == 0 ]] || note+=", $local_n $verb"
|
|
134
|
-
printf '%s' "$note"
|
|
140
|
+
# "1 commit", "3 commits"
|
|
141
|
+
commits() {
|
|
142
|
+
printf '%s commit%s' "$1" "$([[ "$1" == 1 ]] || printf s)"
|
|
135
143
|
}
|
|
136
144
|
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
# Uncommitted changes to tracked files, staged or not.
|
|
146
|
+
has_tracked_changes() {
|
|
147
|
+
! git -C "$1" diff --quiet HEAD --
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
pull_repo() {
|
|
151
|
+
local repo="$1" rel branch behind ahead out
|
|
139
152
|
rel="$(repo_label "$repo")"
|
|
153
|
+
prepare "$repo" "$rel" || return 0
|
|
140
154
|
|
|
141
|
-
if
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
155
|
+
if [[ "$behind" == 0 ]]; then
|
|
156
|
+
report_current "$rel" "$branch"
|
|
157
|
+
elif [[ "$ahead" == 0 ]]; then
|
|
158
|
+
# git refuses, touching nothing, when local changes are in the way
|
|
159
|
+
if out="$(git -C "$repo" merge --ff-only --quiet '@{upstream}' 2>&1)"; then
|
|
160
|
+
report_updated "$rel" "$branch" "$behind" 0 ""
|
|
161
|
+
elif [[ -n "$(git -C "$repo" status --porcelain)" ]]; then
|
|
162
|
+
report_dirty "$rel" "$branch" "$behind"
|
|
163
|
+
else
|
|
164
|
+
report_failed "$rel" "$branch" "$out"
|
|
165
|
+
fi
|
|
166
|
+
elif [[ "$rebase" == 0 ]]; then
|
|
167
|
+
report_diverged "$rel" "$branch" "$behind" "$ahead"
|
|
168
|
+
elif has_tracked_changes "$repo"; then
|
|
169
|
+
report_dirty "$rel" "$branch" "$behind"
|
|
170
|
+
elif out="$(git -C "$repo" rebase --quiet '@{upstream}' 2>&1)"; then
|
|
171
|
+
report_updated "$rel" "$branch" "$behind" "$ahead" rebased
|
|
172
|
+
else
|
|
173
|
+
# A rebase that stopped on a conflict must not leave the repository
|
|
174
|
+
# mid-rebase; --abort succeeds only when one is in progress.
|
|
175
|
+
if git -C "$repo" rebase --abort >/dev/null 2>&1; then
|
|
176
|
+
report_failed "$rel" "$branch" "$out"
|
|
177
|
+
elif [[ -n "$(git -C "$repo" status --porcelain)" ]]; then
|
|
178
|
+
report_dirty "$rel" "$branch" "$behind"
|
|
179
|
+
else
|
|
180
|
+
report_failed "$rel" "$branch" "$out"
|
|
181
|
+
fi
|
|
150
182
|
fi
|
|
183
|
+
}
|
|
151
184
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
185
|
+
# Would local changes stop a fast-forward? Git refuses when a file it has to
|
|
186
|
+
# update is modified locally or exists untracked, so compare the two lists.
|
|
187
|
+
changes_block_update() {
|
|
188
|
+
local repo="$1" incoming local_paths
|
|
189
|
+
incoming="$(git -C "$repo" diff --name-only HEAD '@{upstream}')"
|
|
190
|
+
local_paths="$(git -C "$repo" status --porcelain --untracked-files=all | cut -c4-)"
|
|
191
|
+
[[ -n "$incoming" && -n "$local_paths" ]] || return 1
|
|
192
|
+
printf '%s\n' "$local_paths" | grep -qFxf <(printf '%s\n' "$incoming")
|
|
193
|
+
}
|
|
159
194
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
195
|
+
dryrun_repo() {
|
|
196
|
+
local repo="$1" rel branch behind ahead
|
|
197
|
+
rel="$(repo_label "$repo")"
|
|
198
|
+
prepare "$repo" "$rel" || return 0
|
|
164
199
|
|
|
165
|
-
if [[ "$behind"
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
200
|
+
if [[ "$behind" == 0 ]]; then
|
|
201
|
+
report_current "$rel" "$branch"
|
|
202
|
+
elif [[ "$ahead" == 0 ]]; then
|
|
203
|
+
if changes_block_update "$repo"; then
|
|
204
|
+
report_dirty "$rel" "$branch" "$behind"
|
|
205
|
+
else
|
|
206
|
+
report_updated "$rel" "$branch" "$behind" 0 ""
|
|
207
|
+
fi
|
|
208
|
+
elif [[ "$rebase" == 0 ]]; then
|
|
209
|
+
report_diverged "$rel" "$branch" "$behind" "$ahead"
|
|
210
|
+
elif has_tracked_changes "$repo"; then
|
|
211
|
+
report_dirty "$rel" "$branch" "$behind"
|
|
174
212
|
else
|
|
175
|
-
|
|
176
|
-
printf '%s=%s %s %s(%s)%s\n' "$dim" "$reset" "$rel" "$dim" "$branch" "$reset"
|
|
213
|
+
report_updated "$rel" "$branch" "$behind" "$ahead" "to rebase"
|
|
177
214
|
fi
|
|
178
215
|
}
|
|
179
216
|
|
|
@@ -214,8 +251,8 @@ fi
|
|
|
214
251
|
|
|
215
252
|
[[ "$mode" != dryrun ]] || printf '%sDry run: nothing will be pulled.%s\n' "$dim" "$reset"
|
|
216
253
|
|
|
217
|
-
updated=0 current=0 skipped=0 failed=0
|
|
218
|
-
failed_repos=()
|
|
254
|
+
updated=0 current=0 skipped=0 failed=0 dirty=0
|
|
255
|
+
failed_repos=() dirty_repos=()
|
|
219
256
|
|
|
220
257
|
consume() {
|
|
221
258
|
local text="$1" head="${1%%$'\n'*}" body="${1#*$'\n'}"
|
|
@@ -224,6 +261,7 @@ consume() {
|
|
|
224
261
|
updated) updated=$((updated + 1)) ;;
|
|
225
262
|
current) current=$((current + 1)) ;;
|
|
226
263
|
skipped) skipped=$((skipped + 1)) ;;
|
|
264
|
+
dirty) dirty=$((dirty + 1)); dirty_repos+=("$label") ;;
|
|
227
265
|
failed) failed=$((failed + 1)); failed_repos+=("$label") ;;
|
|
228
266
|
*) echo "pullr: unexpected worker result: $text" >&2; exit 1 ;;
|
|
229
267
|
esac
|
|
@@ -235,7 +273,7 @@ if ((jobs > 1 && count > 1)); then
|
|
|
235
273
|
trap 'rm -rf "$scratch"' EXIT
|
|
236
274
|
export GIT_TERMINAL_PROMPT=0
|
|
237
275
|
export mode rebase root green yellow red dim reset
|
|
238
|
-
export -f worker pull_repo dryrun_repo repo_label indented
|
|
276
|
+
export -f worker pull_repo dryrun_repo repo_label indented prepare report_failed report_diverged report_dirty report_current report_updated commits has_tracked_changes changes_block_update
|
|
239
277
|
# shellcheck disable=SC2016 # the single-quoted body runs inside the child shell
|
|
240
278
|
for ((i = 0; i < count; i++)); do printf '%s\0%s\0' "$i" "${repos[i]}"; done |
|
|
241
279
|
xargs -0 -n 2 -P "$jobs" "$BASH" -c 'worker "$@"' _ "$scratch" &
|
|
@@ -260,12 +298,13 @@ fi
|
|
|
260
298
|
|
|
261
299
|
echo
|
|
262
300
|
if [[ "$mode" == dryrun ]]; then
|
|
263
|
-
printf 'Repos: %d %swould update: %d%s up to date: %d %
|
|
264
|
-
"$count" "$green" "$updated" "$reset" "$current" "$yellow" "$skipped" "$reset" "$red" "$failed" "$reset"
|
|
301
|
+
printf 'Repos: %d %swould update: %d%s up to date: %d %sdirty: %d skipped: %d%s %swould fail: %d%s\n' \
|
|
302
|
+
"$count" "$green" "$updated" "$reset" "$current" "$yellow" "$dirty" "$skipped" "$reset" "$red" "$failed" "$reset"
|
|
265
303
|
else
|
|
266
|
-
printf 'Repos: %d %supdated: %d%s up to date: %d %
|
|
267
|
-
"$count" "$green" "$updated" "$reset" "$current" "$yellow" "$skipped" "$reset" "$red" "$failed" "$reset"
|
|
304
|
+
printf 'Repos: %d %supdated: %d%s up to date: %d %sdirty: %d skipped: %d%s %sfailed: %d%s\n' \
|
|
305
|
+
"$count" "$green" "$updated" "$reset" "$current" "$yellow" "$dirty" "$skipped" "$reset" "$red" "$failed" "$reset"
|
|
268
306
|
fi
|
|
307
|
+
((dirty == 0)) || printf 'Dirty: %s\n' "${dirty_repos[*]}"
|
|
269
308
|
if ((failed)); then
|
|
270
309
|
printf 'Failed: %s\n' "${failed_repos[*]}"
|
|
271
310
|
exit 1
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kova1/pullr",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Safe, fast-forward pull every git repository under a directory",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"git",
|
|
7
7
|
"pull",
|