Skip to content

Commit 898b250

Browse files
authored
Merge pull request #982 from kane8n/add-filter-flag
Add "--filter" flag to enable git partial clone
2 parents fb0e300 + d8d13af commit 898b250

3 files changed

Lines changed: 116 additions & 1 deletion

File tree

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ More documentation on specific topics can be [found here](./docs).
145145
## Manual
146146

147147
```
148+
148149
GIT-SYNC
149150
150151
NAME
@@ -296,6 +297,14 @@ OPTIONS
296297
The timeout for the --exechook-command. If not specifid, this
297298
defaults to 30 seconds ("30s").
298299
300+
--filter <string>, $GITSYNC_FILTER
301+
Use partial clone with the specified filter. This can reduce
302+
the amount of data transferred when cloning large repositories.
303+
Common values are 'blob:none' (omit all blobs, fetch on demand)
304+
and 'tree:0' (omit all trees and blobs). This is most effective
305+
when combined with --depth and --sparse-checkout-file. See
306+
https://git-scm.com/docs/partial-clone for more information.
307+
299308
--git <string>, $GITSYNC_GIT
300309
The git command to run (subject to PATH search, mostly for
301310
testing). This defaults to "git".
@@ -425,7 +434,10 @@ OPTIONS
425434
--password-file <string>, $GITSYNC_PASSWORD_FILE
426435
The file from which the password or personal access token (see
427436
github docs) to use for git authentication (see --username) will be
428-
read. See also $GITSYNC_PASSWORD.
437+
read. The file is re-read before each sync attempt, allowing
438+
git-sync to pick up token rotations automatically (e.g. when using
439+
dynamic credentials from an external secrets system).
440+
See also $GITSYNC_PASSWORD.
429441
430442
--period <duration>, $GITSYNC_PERIOD
431443
How long to wait between sync attempts. This must be at least

main.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ type repoSync struct {
120120
repo string // remote repo to sync
121121
ref string // the ref to sync
122122
depth int // for shallow sync
123+
filter string // for partial clone
123124
submodules submodulesMode // how to handle submodules
124125
gc gcMode // garbage collection
125126
link absPath // absolute path to the symlink to publish
@@ -167,6 +168,9 @@ func main() {
167168
flDepth := pflag.Int("depth",
168169
envInt(1, "GITSYNC_DEPTH", "GIT_SYNC_DEPTH"),
169170
"create a shallow clone with history truncated to the specified number of commits")
171+
flFilter := pflag.String("filter",
172+
envString("", "GITSYNC_FILTER"),
173+
"use partial clone with the specified filter (e.g. 'blob:none', 'tree:0')")
170174
flSubmodules := pflag.String("submodules",
171175
envString("recursive", "GITSYNC_SUBMODULES", "GIT_SYNC_SUBMODULES"),
172176
"git submodule behavior: one of 'recursive', 'shallow', or 'off'")
@@ -725,6 +729,7 @@ func main() {
725729
repo: *flRepo,
726730
ref: *flRef,
727731
depth: *flDepth,
732+
filter: *flFilter,
728733
submodules: submodulesMode(*flSubmodules),
729734
gc: gcMode(*flGitGC),
730735
link: absLink,
@@ -1893,6 +1898,9 @@ func (git *repoSync) fetch(ctx context.Context, ref string) error {
18931898
args = append(args, "--unshallow")
18941899
}
18951900
}
1901+
if git.filter != "" {
1902+
args = append(args, "--filter", git.filter)
1903+
}
18961904
if _, _, err := git.Run(ctx, git.root, args...); err != nil {
18971905
return err
18981906
}
@@ -2520,6 +2528,14 @@ OPTIONS
25202528
The timeout for the --exechook-command. If not specifid, this
25212529
defaults to 30 seconds ("30s").
25222530
2531+
--filter <string>, $GITSYNC_FILTER
2532+
Use partial clone with the specified filter. This can reduce
2533+
the amount of data transferred when cloning large repositories.
2534+
Common values are 'blob:none' (omit all blobs, fetch on demand)
2535+
and 'tree:0' (omit all trees and blobs). This is most effective
2536+
when combined with --depth and --sparse-checkout-file. See
2537+
https://git-scm.com/docs/partial-clone for more information.
2538+
25232539
--git <string>, $GITSYNC_GIT
25242540
The git command to run (subject to PATH search, mostly for
25252541
testing). This defaults to "git".

test_e2e.sh

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3345,6 +3345,93 @@ function e2e::sparse_checkout() {
33453345
assert_file_eq "$ROOT/link/file2" "${FUNCNAME[0]}"
33463346
}
33473347

3348+
##############################################
3349+
# Test filter (partial clone) with blob:none
3350+
##############################################
3351+
function e2e::filter_partial_clone_blob_none() {
3352+
GIT_SYNC \
3353+
--one-time \
3354+
--repo="file://$REPO" \
3355+
--root="$ROOT" \
3356+
--link="link" \
3357+
--filter="blob:none" \
3358+
--depth=0
3359+
assert_link_exists "$ROOT/link"
3360+
assert_file_exists "$ROOT/link/file"
3361+
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
3362+
3363+
# Verify partial clone is in effect: git fetch --filter creates a
3364+
# .promisor marker alongside each pack file containing filtered objects.
3365+
local pack_dir
3366+
pack_dir="$(git -C "$ROOT/link" rev-parse --git-common-dir)/objects/pack"
3367+
if ! ls "$pack_dir"/*.promisor >/dev/null 2>&1; then
3368+
fail "expected .promisor pack marker (partial clone)"
3369+
fi
3370+
}
3371+
3372+
##############################################
3373+
# Test filter with sparse-checkout
3374+
##############################################
3375+
function e2e::filter_with_sparse_checkout() {
3376+
echo "!/*" > "$WORK/sparseconfig"
3377+
echo "!/*/" >> "$WORK/sparseconfig"
3378+
echo "file2" >> "$WORK/sparseconfig"
3379+
echo "${FUNCNAME[0]}" > "$REPO/file2"
3380+
mkdir -p "$REPO/dir"
3381+
echo "${FUNCNAME[0]}" > "$REPO/dir/file3"
3382+
git -C "$REPO" add file2
3383+
git -C "$REPO" add dir
3384+
git -C "$REPO" commit -qam "${FUNCNAME[0]}"
3385+
3386+
GIT_SYNC \
3387+
--one-time \
3388+
--repo="file://$REPO" \
3389+
--root="$ROOT" \
3390+
--link="link" \
3391+
--filter="blob:none" \
3392+
--depth=1 \
3393+
--sparse-checkout-file="$WORK/sparseconfig"
3394+
assert_link_exists "$ROOT/link"
3395+
assert_file_exists "$ROOT/link/file2"
3396+
assert_file_absent "$ROOT/link/dir/file3"
3397+
assert_file_absent "$ROOT/link/dir"
3398+
assert_file_eq "$ROOT/link/file2" "${FUNCNAME[0]}"
3399+
}
3400+
3401+
##############################################
3402+
# Test filter syncing across updates
3403+
##############################################
3404+
function e2e::filter_across_updates() {
3405+
# First sync
3406+
echo "${FUNCNAME[0]} 1" > "$REPO/file"
3407+
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
3408+
3409+
GIT_SYNC \
3410+
--period=100ms \
3411+
--repo="file://$REPO" \
3412+
--filter="blob:none" \
3413+
--depth=0 \
3414+
--root="$ROOT" \
3415+
--link="link" \
3416+
&
3417+
wait_for_sync "${MAXWAIT}"
3418+
assert_link_exists "$ROOT/link"
3419+
assert_file_exists "$ROOT/link/file"
3420+
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
3421+
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
3422+
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
3423+
3424+
# Move forward
3425+
echo "${FUNCNAME[0]} 2" > "$REPO/file"
3426+
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
3427+
wait_for_sync "${MAXWAIT}"
3428+
assert_link_exists "$ROOT/link"
3429+
assert_file_exists "$ROOT/link/file"
3430+
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
3431+
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
3432+
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
3433+
}
3434+
33483435
##############################################
33493436
# Test additional git configs
33503437
##############################################

0 commit comments

Comments
 (0)