push: add ability to read values from stdin

Read refs from stdin:
`printf "branch\ntag" | git lfs push <remote> --stdin`

Read object IDs from stdin:
`printf "c0ffee..." | git lfs push --object-id <remote> --stdin`

Values read from stdin are newline-delimited.
This commit is contained in:
Robert Coup 2022-08-12 12:18:47 +01:00
parent 7001116051
commit e35f407d6a
3 changed files with 105 additions and 10 deletions

@ -1,6 +1,7 @@
package commands
import (
"bufio"
"os"
"github.com/git-lfs/git-lfs/v3/errors"
@ -21,12 +22,15 @@ var (
// shares some global vars and functions with command_pre_push.go
)
// pushCommand pushes local objects to a Git LFS server. It takes two
// arguments:
// pushCommand pushes local objects to a Git LFS server. It has four forms:
//
// `<remote> <remote ref>`
// `<remote> <ref>...`
// `<remote> --stdin` (reads refs from stdin)
// `<remote> --object-id <oid>...`
// `<remote> --object-id --stdin` (reads oids from stdin)
//
// Remote must be a remote name, not a URL
// Remote must be a remote name, not a URL. With --stdin, values are newline
// separated.
//
// pushCommand calculates the git objects to send by comparing the range
// of commits between the local and remote git servers.
@ -44,15 +48,36 @@ func pushCommand(cmd *cobra.Command, args []string) {
}
ctx := newUploadContext(pushDryRun)
if pushObjectIDs {
if len(args) < 2 {
Print(tr.Tr.Get("At least one object ID must be supplied with --object-id"))
return
var argList []string
if useStdin {
if len(args) > 1 {
Print(tr.Tr.Get("Further command line arguments are ignored with --stdin"))
os.Exit(1)
}
uploadsWithObjectIDs(ctx, args[1:])
scanner := bufio.NewScanner(os.Stdin) // line-delimited
for scanner.Scan() {
line := scanner.Text()
if line != "" {
argList = append(argList, line)
}
}
if err := scanner.Err(); err != nil {
ExitWithError(errors.Wrap(err, tr.Tr.Get("Error reading from stdin:")))
}
} else {
uploadsBetweenRefAndRemote(ctx, args[1:])
argList = args[1:]
}
if pushObjectIDs {
if len(argList) < 1 {
Print(tr.Tr.Get("At least one object ID must be supplied with --object-id"))
os.Exit(1)
}
uploadsWithObjectIDs(ctx, argList)
} else {
uploadsBetweenRefAndRemote(ctx, argList)
}
}
@ -137,6 +162,7 @@ func init() {
RegisterCommand("push", pushCommand, func(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&pushDryRun, "dry-run", "d", false, "Do everything except actually send the updates")
cmd.Flags().BoolVarP(&pushObjectIDs, "object-id", "o", false, "Push LFS object ID(s)")
cmd.Flags().BoolVarP(&useStdin, "stdin", "", false, "Read object IDs or refs from stdin")
cmd.Flags().BoolVarP(&pushAll, "all", "a", false, "Push all objects for the current ref to the remote.")
})
}

@ -8,7 +8,9 @@ git-lfs-push - Push queued large files to the Git LFS endpoint
`git lfs push` [options] <remote> [<ref>...] +
`git lfs push` <remote> [<ref>...] +
`git lfs push` [options] <remote> --stdin
`git lfs push` --object-id <remote> [<oid>...]
`git lfs push` --object-id <remote> --stdin
== DESCRIPTION
@ -32,6 +34,9 @@ by the local clone of the remote.
`--object-id`::
This pushes only the object OIDs listed at the end of the command, separated
by spaces.
`--stdin`::
Read a list of newline-delimited refs (or object IDs when using `--object-id`)
from standard input instead of the command line.
== SEE ALSO

@ -66,6 +66,17 @@ begin_test "push with given remote, configured pushRemote"
)
end_test
begin_test "push via stdin with extra arguments"
(
set -e
push_repo_setup "push-stdin-extra-args"
echo "main" | git lfs push origin --stdin --dry-run "another-ref" \
2>&1 | tee push.log
grep "Further command line arguments are ignored with --stdin" push.log
)
begin_test "push"
(
set -e
@ -98,6 +109,11 @@ begin_test "push"
grep "push 82be50ad35070a4ef3467a0a650c52d5b637035e7ad02c36652e59d01ba282b7 => b.dat" push.log
[ $(grep -c "^push " < push.log) -eq 2 ]
printf "push-b\n\n" | git lfs push --dry-run origin --stdin 2>&1 | tee push.log
grep "push 4c48d2a6991c9895bcddcf027e1e4907280bcf21975492b1afbade396d6a3340 => a.dat" push.log
grep "push 82be50ad35070a4ef3467a0a650c52d5b637035e7ad02c36652e59d01ba282b7 => b.dat" push.log
[ $(grep -c "^push " < push.log) -eq 2 ]
# simulate remote ref
mkdir -p .git/refs/remotes/origin
git rev-parse HEAD > .git/refs/remotes/origin/HEAD
@ -301,6 +317,13 @@ begin_test "push --all (multiple ref args)"
grep "push $oid4 => file1.dat" push.log
[ $(grep -c "^push " push.log) -eq 4 ]
printf "branch\ntag" | git lfs push --dry-run --all origin --stdin 2>&1 | tee push.log
grep "push $oid1 => file1.dat" push.log
grep "push $oid2 => file1.dat" push.log
grep "push $oid3 => file1.dat" push.log
grep "push $oid4 => file1.dat" push.log
[ $(grep -c "^push " push.log) -eq 4 ]
git lfs push --all origin branch tag 2>&1 | tee push.log
[ $(grep -c "Uploading LFS objects: 100% (4/4)" push.log) -eq 1 ]
assert_server_object "$reponame-$suffix" "$oid1"
@ -426,6 +449,47 @@ begin_test "push object id(s)"
)
end_test
begin_test "push object id(s) via stdin"
(
set -e
reponame="$(basename "$0" ".sh")"
setup_remote_repo "$reponame"
clone_repo "$reponame" repo3
git config "lfs.$(repo_endpoint "$GITSERVER" "$reponame").locksverify" true
git lfs track "*.dat"
echo "push a" > a.dat
git add .gitattributes a.dat
git commit -m "add a.dat"
echo "" | git lfs push --object-id origin --stdin --dry-run \
2>&1 | tee push.log
grep "At least one object ID must be supplied with --object-id" push.log
echo "4c48d2a6991c9895bcddcf027e1e4907280bcf21975492b1afbade396d6a3340" | \
git lfs push --object-id origin --stdin --dry-run "c0ffee" \
2>&1 | tee push.log
grep "Further command line arguments are ignored with --stdin" push.log
echo "4c48d2a6991c9895bcddcf027e1e4907280bcf21975492b1afbade396d6a3340" | \
git lfs push --object-id origin --stdin --dry-run \
2>&1 | tee push.log
grep "push 4c48d2a6991c9895bcddcf027e1e4907280bcf21975492b1afbade396d6a3340 =>" push.log
echo "push b" > b.dat
git add b.dat
git commit -m "add b.dat"
printf "4c48d2a6991c9895bcddcf027e1e4907280bcf21975492b1afbade396d6a3340\n82be50ad35070a4ef3467a0a650c52d5b637035e7ad02c36652e59d01ba282b7\n\n" | \
git lfs push --object-id origin --stdin --dry-run \
2>&1 | tee push.log
grep "push 4c48d2a6991c9895bcddcf027e1e4907280bcf21975492b1afbade396d6a3340 =>" push.log
grep "push 82be50ad35070a4ef3467a0a650c52d5b637035e7ad02c36652e59d01ba282b7 =>" push.log
)
end_test
begin_test "push modified files"
(
set -e