Merge branch 'master' into ttaylorr/makefile

This commit is contained in:
Taylor Blau 2018-07-23 12:44:24 -05:00
commit 813fdbdb52
130 changed files with 1437 additions and 742 deletions

4
.gitignore vendored

@ -8,7 +8,9 @@ man/*
*.test
tmp
test/remote
t/remote
t/test_count
t/test_count.lock
debian/git-lfs/
debian/*.log

@ -112,7 +112,7 @@ submitting changes, be sure to run the Go tests and the shell integration
tests:
$ make test # runs just the Go tests
$ script/integration # runs the shell tests in ./test
$ cd t && make test # runs the shell tests in ./test
$ script/cibuild # runs everything, with verbose debug output
## Updating 3rd party packages

@ -29,8 +29,9 @@ setup and preferences.
[PackageCloud](https://packagecloud.io/github/git-lfs/install).
* **macOS users**. [Homebrew](https://brew.sh) bottles are distributed, and can
be installed via `brew install git-lfs`.
* **Windows users**. Chocolatey packages are distributed, and can be installed
via `choco install git-lfs`.
* **Windows users**. Git LFS is included in the distribution of
[Git for Windows](https://gitforwindows.org/). Alternatively, you can
install a recent version of Git LFS from the Chocolatey package manager.
In addition, [binary packages](https://github.com/git-lfs/git-lfs/releases) are
available for Linux, macOS, Windows, and FreeBSD. This repository can also be

@ -1,8 +1,12 @@
image: Visual Studio 2017
skip_branch_with_pr: true
environment:
CYGWIN: "$(CYGWIN) winsymlinks:nativestrict"
MSYS: "$(MSYS) winsymlinks:nativestrict"
GIT_LFS_NO_TEST_COUNT: 1
GIT_LFS_LOCK_ACQUIRE_DISABLED: 1
GOPATH: $(HOMEDRIVE)$(HOMEPATH)\go
MSYSTEM: MINGW64
@ -10,8 +14,13 @@ clone_folder: $(GOPATH)\src\github.com\git-lfs\git-lfs
install:
- rd C:\Go /s /q
- rd C:\Perl /s /q
- refreshenv
- cinst golang --version 1.8.3 -y
- cinst InnoSetup -y
- cinst strawberryperl -y
- refreshenv
- cinst make
- refreshenv
- ps: |
echo "Go directories in machine PATH environment:"

@ -1,7 +1,9 @@
package commands
import (
"bufio"
"fmt"
"io"
"path/filepath"
"strings"
@ -21,6 +23,10 @@ var (
// in the migration.
migrateExcludeRefs []string
// migrateYes indicates that an answer of 'yes' should be presumed
// whenever 'git lfs migrate' asks for user input.
migrateYes bool
// migrateSkipFetch assumes that the client has the latest copy of
// remote references, and thus should not contact the remote for a set
// of updated references.
@ -173,13 +179,31 @@ func includeExcludeRefs(l *tasklog.Logger, args []string) (include, exclude []st
include = append(include, migrateIncludeRefs...)
exclude = append(exclude, migrateExcludeRefs...)
} else if migrateEverything {
localRefs, err := git.LocalRefs()
refs, err := git.AllRefsIn("")
if err != nil {
return nil, nil, err
}
for _, ref := range localRefs {
include = append(include, ref.Refspec())
for _, ref := range refs {
switch ref.Type {
case git.RefTypeLocalBranch, git.RefTypeLocalTag,
git.RefTypeRemoteBranch, git.RefTypeRemoteTag:
include = append(include, ref.Refspec())
case git.RefTypeOther:
parts := strings.SplitN(ref.Refspec(), "/", 3)
if len(parts) < 2 {
continue
}
switch parts[1] {
// The following are GitLab-, GitHub-, VSTS-,
// and BitBucket-specific reference naming
// conventions.
case "merge-requests", "pull", "pull-requests":
include = append(include, ref.Refspec())
}
}
}
} else {
bare, err := git.IsBare()
@ -294,6 +318,56 @@ func getHistoryRewriter(cmd *cobra.Command, db *gitobj.ObjectDatabase, l *tasklo
githistory.WithFilter(filter), githistory.WithLogger(l))
}
func ensureWorkingCopyClean(in io.Reader, out io.Writer) {
dirty, err := git.IsWorkingCopyDirty()
if err != nil {
ExitWithError(errors.Wrap(err,
"fatal: could not determine if working copy is dirty"))
}
if !dirty {
return
}
var proceed bool
if migrateYes {
proceed = true
} else {
answer := bufio.NewReader(in)
L:
for {
fmt.Fprintf(out, "migrate: override changes in your working copy? [Y/n] ")
s, err := answer.ReadString('\n')
if err != nil {
if err == io.EOF {
break L
}
ExitWithError(errors.Wrap(err,
"fatal: could not read answer"))
}
switch strings.TrimSpace(s) {
case "n", "N":
proceed = false
break L
case "y", "Y":
proceed = true
break L
}
if !strings.HasSuffix(s, "\n") {
fmt.Fprintf(out, "\n")
}
}
}
if proceed {
fmt.Fprintf(out, "migrate: changes in your working copy will be overridden ...\n")
} else {
Exit("migrate: working copy must not be dirty")
}
}
func init() {
info := NewCommand("info", migrateInfoCommand)
info.Flags().IntVar(&migrateInfoTopN, "top", 5, "--top=<n>")
@ -321,6 +395,8 @@ func init() {
cmd.PersistentFlags().BoolVar(&migrateEverything, "everything", false, "Migrate all local references")
cmd.PersistentFlags().BoolVar(&migrateSkipFetch, "skip-fetch", false, "Assume up-to-date remote references.")
cmd.PersistentFlags().BoolVarP(&migrateYes, "yes", "y", false, "Don't prompt for answers.")
cmd.AddCommand(exportCmd, importCmd, info)
})
}

@ -17,6 +17,8 @@ import (
)
func migrateExportCommand(cmd *cobra.Command, args []string) {
ensureWorkingCopyClean(os.Stdin, os.Stderr)
l := tasklog.NewLogger(os.Stderr)
defer l.Close()

@ -22,6 +22,8 @@ import (
)
func migrateImportCommand(cmd *cobra.Command, args []string) {
ensureWorkingCopyClean(os.Stdin, os.Stderr)
l := tasklog.NewLogger(os.Stderr)
defer l.Close()

@ -38,7 +38,7 @@ func pushCommand(cmd *cobra.Command, args []string) {
requireGitVersion()
// Remote is first arg
if err := cfg.SetValidRemote(args[0]); err != nil {
if err := cfg.SetValidPushRemote(args[0]); err != nil {
Exit("Invalid remote name %q: %s", args[0], err)
}

@ -232,10 +232,22 @@ func (c *Configuration) SetValidRemote(name string) error {
return nil
}
func (c *Configuration) SetValidPushRemote(name string) error {
if err := git.ValidateRemote(name); err != nil {
return err
}
c.SetPushRemote(name)
return nil
}
func (c *Configuration) SetRemote(name string) {
c.currentRemote = &name
}
func (c *Configuration) SetPushRemote(name string) {
c.pushRemote = &name
}
func (c *Configuration) Remotes() []string {
c.loadGitConfig()
return c.remotes
@ -311,8 +323,8 @@ func (c *Configuration) LocalGitStorageDir() string {
return c.Filesystem().GitStorageDir
}
func (c *Configuration) LocalReferenceDir() string {
return c.Filesystem().ReferenceDir
func (c *Configuration) LocalReferenceDirs() []string {
return c.Filesystem().ReferenceDirs
}
func (c *Configuration) LFSStorageDir() string {
@ -346,7 +358,12 @@ func (c *Configuration) Filesystem() *fs.Filesystem {
if c.fs == nil {
lfsdir, _ := c.Git.Get("lfs.storage")
c.fs = fs.New(c.LocalGitDir(), c.LocalWorkingDir(), lfsdir)
c.fs = fs.New(
c.Os,
c.LocalGitDir(),
c.LocalWorkingDir(),
lfsdir,
)
}
return c.fs

@ -158,6 +158,7 @@ func keyIsUnsafe(key string) bool {
}
var safeKeys = []string{
"lfs.allowincompletepush",
"lfs.fetchexclude",
"lfs.fetchinclude",
"lfs.gitprotocol",

@ -190,8 +190,8 @@ The following configuration:
Would, therefore, include commits: F, E, D, C, B, but exclude commit A.
The presence of flag `--everything` indicates that all local references should be
migrated.
The presence of flag `--everything` indicates that all local and remote
references should be migrated.
## EXAMPLES
@ -253,16 +253,20 @@ Note: This will require a force push to any existing Git remotes.
### Migrate without rewriting local history
You can also migrate files without modifying the existing history of your respoitory:
You can also migrate files without modifying the existing history of your
repository:
Without a specified commit message:
```
git lfs migrate import --no-rewrite test.zip *.mp3 *.psd
$ git lfs migrate import --no-rewrite test.zip *.mp3 *.psd
```
With a specified commit message:
```
git lfs migrate import --no-rewrite -m "Import .zip, .mp3, .psd files" \
test.zip *.mpd *.psd
$ git lfs migrate import --no-rewrite -m "Import .zip, .mp3, .psd files" \
test.zip *.mpd *.psd
```
## SEE ALSO

108
fs/fs.go

@ -1,6 +1,7 @@
package fs
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
@ -12,10 +13,19 @@ import (
"sync"
"github.com/git-lfs/git-lfs/tools"
"github.com/rubyist/tracerx"
)
var oidRE = regexp.MustCompile(`\A[[:alnum:]]{64}`)
// Environment is a copy of a subset of the interface
// github.com/git-lfs/git-lfs/config.Environment.
//
// For more information, see config/environment.go.
type Environment interface {
Get(key string) (val string, ok bool)
}
// Object represents a locally stored LFS object.
type Object struct {
Oid string
@ -23,9 +33,9 @@ type Object struct {
}
type Filesystem struct {
GitStorageDir string // parent of objects/lfs (may be same as GitDir but may not)
LFSStorageDir string // parent of lfs objects and tmp dirs. Default: ".git/lfs"
ReferenceDir string // alternative local media dir (relative to clone reference repo)
GitStorageDir string // parent of objects/lfs (may be same as GitDir but may not)
LFSStorageDir string // parent of lfs objects and tmp dirs. Default: ".git/lfs"
ReferenceDirs []string // alternative local media dirs (relative to clone reference repo)
lfsobjdir string
tmpdir string
logdir string
@ -105,12 +115,16 @@ func (f *Filesystem) localObjectDir(oid string) string {
return filepath.Join(f.LFSObjectDir(), oid[0:2], oid[2:4])
}
func (f *Filesystem) ObjectReferencePath(oid string) string {
if len(f.ReferenceDir) == 0 {
return f.ReferenceDir
func (f *Filesystem) ObjectReferencePaths(oid string) []string {
if len(f.ReferenceDirs) == 0 {
return nil
}
return filepath.Join(f.ReferenceDir, oid[0:2], oid[2:4], oid)
var paths []string
for _, ref := range f.ReferenceDirs {
paths = append(paths, filepath.Join(ref, oid[0:2], oid[2:4], oid))
}
return paths
}
func (f *Filesystem) LFSObjectDir() string {
@ -159,12 +173,12 @@ func (f *Filesystem) Cleanup() error {
// New initializes a new *Filesystem with the given directories. gitdir is the
// path to the bare repo, workdir is the path to the repository working
// directory, and lfsdir is the optional path to the `.git/lfs` directory.
func New(gitdir, workdir, lfsdir string) *Filesystem {
func New(env Environment, gitdir, workdir, lfsdir string) *Filesystem {
fs := &Filesystem{
GitStorageDir: resolveGitStorageDir(gitdir),
}
fs.ReferenceDir = resolveReferenceDir(fs.GitStorageDir)
fs.ReferenceDirs = resolveReferenceDirs(env, fs.GitStorageDir)
if len(lfsdir) == 0 {
lfsdir = "lfs"
@ -179,19 +193,75 @@ func New(gitdir, workdir, lfsdir string) *Filesystem {
return fs
}
func resolveReferenceDir(gitStorageDir string) string {
cloneReferencePath := filepath.Join(gitStorageDir, "objects", "info", "alternates")
if tools.FileExists(cloneReferencePath) {
buffer, err := ioutil.ReadFile(cloneReferencePath)
if err == nil {
path := strings.TrimSpace(string(buffer[:]))
referenceLfsStoragePath := filepath.Join(filepath.Dir(path), "lfs", "objects")
if tools.DirExists(referenceLfsStoragePath) {
return referenceLfsStoragePath
func resolveReferenceDirs(env Environment, gitStorageDir string) []string {
var references []string
envAlternates, ok := env.Get("GIT_ALTERNATE_OBJECT_DIRECTORIES")
if ok {
splits := strings.Split(envAlternates, string(os.PathListSeparator))
for _, split := range splits {
if dir, ok := existsAlternate(split); ok {
references = append(references, dir)
}
}
}
return ""
cloneReferencePath := filepath.Join(gitStorageDir, "objects", "info", "alternates")
if tools.FileExists(cloneReferencePath) {
f, err := os.Open(cloneReferencePath)
if err != nil {
tracerx.Printf("could not open %s: %s",
cloneReferencePath, err)
return nil
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
text := strings.TrimSpace(scanner.Text())
if len(text) == 0 || strings.HasPrefix(text, "#") {
continue
}
if dir, ok := existsAlternate(text); ok {
references = append(references, dir)
}
}
if err := scanner.Err(); err != nil {
tracerx.Printf("could not scan %s: %s",
cloneReferencePath, err)
}
}
return references
}
// existsAlternate takes an object directory given in "objs" (read as a single,
// line from .git/objects/info/alternates). If that is a satisfiable alternates
// directory (i.e., it exists), the directory is returned along with "true". If
// not, the empty string and false is returned instead.
func existsAlternate(objs string) (string, bool) {
objs = strings.TrimSpace(objs)
if strings.HasPrefix(objs, "\"") {
var err error
unquote := strings.LastIndex(objs, "\"")
if unquote == 0 {
return "", false
}
objs, err = strconv.Unquote(objs[:unquote+1])
if err != nil {
return "", false
}
}
storage := filepath.Join(filepath.Dir(objs), "lfs", "objects")
if tools.DirExists(storage) {
return storage, true
}
return "", false
}
// From a git dir, get the location that objects are to be stored (we will store lfs alongside)

@ -1192,3 +1192,21 @@ func IsFileModified(filepath string) (bool, error) {
return matched, nil
}
// IsWorkingCopyDirty returns true if and only if the working copy in which the
// command was executed is dirty as compared to the index.
//
// If the status of the working copy could not be determined, an error will be
// returned instead.
func IsWorkingCopyDirty() (bool, error) {
bare, err := IsBare()
if bare || err != nil {
return false, err
}
out, err := gitSimple("status", "--porcelain")
if err != nil {
return false, err
}
return len(out) != 0, nil
}

@ -9,7 +9,7 @@ import (
"time"
. "github.com/git-lfs/git-lfs/git"
"github.com/git-lfs/git-lfs/test"
test "github.com/git-lfs/git-lfs/t/cmd/util"
"github.com/stretchr/testify/assert"
)

@ -16,6 +16,18 @@ import (
func (f *GitFilter) SmudgeToFile(filename string, ptr *Pointer, download bool, manifest *tq.Manifest, cb tools.CopyCallback) error {
os.MkdirAll(filepath.Dir(filename), 0755)
if stat, _ := os.Stat(filename); stat != nil && stat.Mode()&0200 == 0 {
if err := os.Chmod(filename, stat.Mode()|0200); err != nil {
return errors.Wrap(err,
"Could not restore write permission")
}
// When we're done, return the file back to its normal
// permission bits.
defer os.Chmod(filename, stat.Mode())
}
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("Could not create working directory file: %v", err)

@ -35,12 +35,14 @@ func Environ(cfg *config.Configuration, manifest *tq.Manifest) []string {
fetchPruneConfig := NewFetchPruneConfig(cfg.Git)
references := strings.Join(cfg.LocalReferenceDirs(), ", ")
env = append(env,
fmt.Sprintf("LocalWorkingDir=%s", cfg.LocalWorkingDir()),
fmt.Sprintf("LocalGitDir=%s", cfg.LocalGitDir()),
fmt.Sprintf("LocalGitStorageDir=%s", cfg.LocalGitStorageDir()),
fmt.Sprintf("LocalMediaDir=%s", cfg.LFSObjectDir()),
fmt.Sprintf("LocalReferenceDir=%s", cfg.LocalReferenceDir()),
fmt.Sprintf("LocalReferenceDirs=%s", references),
fmt.Sprintf("TempDir=%s", cfg.TempDir()),
fmt.Sprintf("ConcurrentTransfers=%d", api.ConcurrentTransfers),
fmt.Sprintf("TusTransfers=%v", cfg.TusTransfersAllowed()),
@ -98,13 +100,19 @@ func LinkOrCopyFromReference(cfg *config.Configuration, oid string, size int64)
if cfg.LFSObjectExists(oid, size) {
return nil
}
altMediafile := cfg.Filesystem().ObjectReferencePath(oid)
altMediafiles := cfg.Filesystem().ObjectReferencePaths(oid)
mediafile, err := cfg.Filesystem().ObjectPath(oid)
if err != nil {
return err
}
if altMediafile != "" && tools.FileExistsOfSize(altMediafile, size) {
return LinkOrCopy(cfg, altMediafile, mediafile)
for _, altMediafile := range altMediafiles {
tracerx.Printf("altMediafile: %s", altMediafile)
if altMediafile != "" && tools.FileExistsOfSize(altMediafile, size) {
err = LinkOrCopy(cfg, altMediafile, mediafile)
if err == nil {
break
}
}
}
return nil
return err
}

@ -7,7 +7,7 @@ import (
"github.com/git-lfs/git-lfs/fs"
"github.com/git-lfs/git-lfs/lfs"
"github.com/git-lfs/git-lfs/test"
test "github.com/git-lfs/git-lfs/t/cmd/util"
"github.com/stretchr/testify/assert"
)

@ -11,7 +11,7 @@ import (
"time"
. "github.com/git-lfs/git-lfs/lfs"
"github.com/git-lfs/git-lfs/test"
test "github.com/git-lfs/git-lfs/t/cmd/util"
"github.com/stretchr/testify/assert"
)

@ -78,6 +78,14 @@ type AskPassCredentialHelper struct {
Program string
}
type credValueType int
const (
credValueTypeUnknown credValueType = iota
credValueTypeUsername
credValueTypePassword
)
// Fill implements fill by running the ASKPASS program and returning its output
// as a password encoded in the Creds type given the key "password".
//
@ -86,60 +94,92 @@ type AskPassCredentialHelper struct {
//
// If there was an error running the command, it is returned instead of a set of
// filled credentials.
//
// The ASKPASS program is only queried if a credential was not already
// provided, i.e. through the git URL
func (a *AskPassCredentialHelper) Fill(what Creds) (Creds, error) {
var user bytes.Buffer
var pass bytes.Buffer
var err bytes.Buffer
u := &url.URL{
Scheme: what["protocol"],
Host: what["host"],
Path: what["path"],
}
// 'ucmd' will run the GIT_ASKPASS (or core.askpass) command prompting
// for a username.
ucmd := exec.Command(a.Program, a.args(fmt.Sprintf("Username for %q", u))...)
ucmd.Stderr = &err
ucmd.Stdout = &user
creds := make(Creds)
tracerx.Printf("creds: filling with GIT_ASKPASS: %s", strings.Join(ucmd.Args, " "))
if err := ucmd.Run(); err != nil {
username, err := a.getValue(what, credValueTypeUsername, u)
if err != nil {
return nil, err
}
creds["username"] = username
if err.Len() > 0 {
return nil, errors.New(err.String())
}
if username := strings.TrimSpace(user.String()); len(username) > 0 {
if len(username) > 0 {
// If a non-empty username was given, add it to the URL via func
// 'net/url.User()'.
u.User = url.User(username)
u.User = url.User(creds["username"])
}
// Regardless, create 'pcmd' to run the GIT_ASKPASS (or core.askpass)
// command prompting for a password.
pcmd := exec.Command(a.Program, a.args(fmt.Sprintf("Password for %q", u))...)
pcmd.Stderr = &err
pcmd.Stdout = &pass
tracerx.Printf("creds: filling with GIT_ASKPASS: %s", strings.Join(pcmd.Args, " "))
if err := pcmd.Run(); err != nil {
password, err := a.getValue(what, credValueTypePassword, u)
if err != nil {
return nil, err
}
creds["password"] = password
return creds, nil
}
func (a *AskPassCredentialHelper) getValue(what Creds, valueType credValueType, u *url.URL) (string, error) {
var valueString string
switch valueType {
case credValueTypeUsername:
valueString = "username"
case credValueTypePassword:
valueString = "password"
default:
return "", errors.Errorf("Invalid Credential type queried from AskPass")
}
// Return the existing credential if it was already provided, otherwise
// query AskPass for it
if given, ok := what[valueString]; ok {
return given, nil
}
return a.getFromProgram(valueType, u)
}
func (a *AskPassCredentialHelper) getFromProgram(valueType credValueType, u *url.URL) (string, error) {
var (
value bytes.Buffer
err bytes.Buffer
valueString string
)
switch valueType {
case credValueTypeUsername:
valueString = "Username"
case credValueTypePassword:
valueString = "Password"
default:
return "", errors.Errorf("Invalid Credential type queried from AskPass")
}
// 'cmd' will run the GIT_ASKPASS (or core.askpass) command prompting
// for the desired valueType (`Username` or `Password`)
cmd := exec.Command(a.Program, a.args(fmt.Sprintf("%s for %q", valueString, u))...)
cmd.Stderr = &err
cmd.Stdout = &value
tracerx.Printf("creds: filling with GIT_ASKPASS: %s", strings.Join(cmd.Args, " "))
if err := cmd.Run(); err != nil {
return "", err
}
if err.Len() > 0 {
return nil, errors.New(err.String())
return "", errors.New(err.String())
}
// Finally, now that we have the username and password information,
// store it in the creds instance that we will return to the caller.
creds := make(Creds)
creds["username"] = strings.TrimSpace(user.String())
creds["password"] = strings.TrimSpace(pass.String())
return creds, nil
return strings.TrimSpace(value.String()), nil
}
// Approve implements CredentialHelper.Approve, and returns nil. The ASKPASS

@ -20,7 +20,7 @@ func TestHttpsProxyFromGitConfig(t *testing.T) {
require.Nil(t, err)
proxyURL, err := proxyFromClient(c)(req)
assert.Equal(t, "proxy-from-env:8080", proxyURL.Host)
assert.Equal(t, "proxy-from-git-config:8080", proxyURL.Host)
assert.Nil(t, err)
}

@ -68,7 +68,7 @@ export SKIPAPITESTCOMPILE=1
pushd src/github.com/git-lfs/%{name}
make test
go get github.com/ThomsonReutersEikon/go-ntlm/ntlm
./script/integration
cd t && make test
popd
rmdir ${GIT_LFS_TEST_DIR}

@ -1,8 +1,26 @@
#!/usr/bin/env bash
set -e
GOCACHE=off script/bootstrap
GOCACHE=off make test
# re-run test to ensure GIT_TRACE output doesn't leak into the git package
GIT_TRACE=1 GOCACHE=off make PKGS=git test
VERBOSE_LOGS=1 script/integration
pushd t >/dev/null
UNAME=$(uname -s)
X=""
if [[ $UNAME == MINGW* || $UNAME == MSYS* || $UNAME == CYGWIN* ]]; then
X=".exe"
fi
PROVE="prove"
PROVE_EXTRA_ARGS="-j9"
if [ -n "$APPVEYOR" ]; then
export PATH="/c/Strawberry/perl/bin:.:$PATH"
PROVE="prove.bat"
PROVE_EXTRA_ARGS="$PROVE_EXTRA_ARGS --exec bash"
fi
VERBOSE_LOGS=1 make X="$X" clean
VERBOSE_LOGS=1 make X="$X" PROVE="$PROVE" PROVE_EXTRA_ARGS="$PROVE_EXTRA_ARGS"
popd >/dev/null

@ -1,44 +0,0 @@
#!/usr/bin/env bash
. "test/testenv.sh"
set -e
SHUTDOWN_LFS=no
SHOW_LOGS=yes
atexit() {
res=${1:-$?}
SHUTDOWN_LFS=yes
if [ "$res" = "0" ]; then
SHOW_LOGS=no
fi
if [ "$SHOW_LOGS" = "yes" ] && [ "$VERBOSE_LOGS" = "1" ]; then
if [ -s "$REMOTEDIR/gitserver.log" ]; then
echo ""
echo "gitserver.log:"
cat "$REMOTEDIR/gitserver.log"
fi
echo ""
echo "env:"
env
fi
shutdown
exit $res
}
trap "atexit" EXIT
if [ -s "$LFS_URL_FILE" ]; then
SHOW_LOGS=no
echo "$LFS_URL_FILE still exists!"
echo "Confirm other tests are done, and run:"
echo " $ curl $(cat "$LFS_URL_FILE")/shutdown"
exit 1
fi
setup
GO15VENDOREXPERIMENT=1 GIT_LFS_TEST_MAXPROCS=$GIT_LFS_TEST_MAXPROCS GIT_LFS_TEST_DIR="$GIT_LFS_TEST_DIR" SHUTDOWN_LFS="no" go run script/*.go -cmd integration "$@"

@ -1,209 +0,0 @@
package main
import (
"bytes"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
"time"
)
var (
bashPath string
debugging = false
erroring = false
maxprocs = 4
testPattern = regexp.MustCompile(`test[/\\]test-([a-z\-]+)\.sh$`)
)
func mainIntegration() {
if len(os.Getenv("DEBUG")) > 0 {
debugging = true
}
setBash()
if max, _ := strconv.Atoi(os.Getenv("GIT_LFS_TEST_MAXPROCS")); max > 0 {
maxprocs = max
}
fmt.Println("Running this maxprocs", maxprocs)
files := testFiles()
if len(files) == 0 {
fmt.Println("no tests to run")
os.Exit(1)
}
var wg sync.WaitGroup
tests := make(chan string, len(files))
output := make(chan string, len(files))
for _, file := range files {
tests <- file
}
close(tests)
outputDone := make(chan bool)
go func() {
for out := range output {
fmt.Println(out)
}
outputDone <- true
}()
for i := 0; i < maxprocs; i++ {
wg.Add(1)
go worker(tests, output, &wg)
}
wg.Wait()
close(output)
<-outputDone
if erroring {
os.Exit(1)
}
}
func runTest(output chan string, testname string) {
buf := &bytes.Buffer{}
cmd := exec.Command(bashPath, testname)
cmd.Stdout = buf
cmd.Stderr = buf
err := cmd.Start()
if err != nil {
sendTestOutput(output, testname, buf, err)
return
}
done := make(chan error)
go func() {
if err := cmd.Wait(); err != nil {
done <- err
}
close(done)
}()
select {
case err = <-done:
sendTestOutput(output, testname, buf, err)
return
case <-time.After(3 * time.Minute):
sendTestOutput(output, testname, buf, errors.New("Timed out"))
cmd.Process.Kill()
return
}
}
func sendTestOutput(output chan string, testname string, buf *bytes.Buffer, err error) {
cli := strings.TrimSpace(buf.String())
if len(cli) == 0 {
cli = fmt.Sprintf("<no output for %s>", testname)
}
if err == nil {
output <- cli
} else {
basetestname := filepath.Base(testname)
if debugging {
fmt.Printf("Error on %s: %s\n", basetestname, err)
}
erroring = true
output <- fmt.Sprintf("error: %s => %s\n%s", basetestname, err, cli)
}
}
func worker(tests <-chan string, output chan string, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case testname, ok := <-tests:
if !ok {
return
}
runTest(output, testname)
}
}
}
func testFiles() []string {
if len(os.Args) < 4 {
return allTestFiles()
}
fileMap := make(map[string]bool)
for _, file := range allTestFiles() {
fileMap[file] = true
}
files := make([]string, 0, len(os.Args)-3)
for _, arg := range os.Args {
fullname := "test/test-" + arg + ".sh"
if fileMap[fullname] {
files = append(files, fullname)
}
}
return files
}
func allTestFiles() []string {
files := make([]string, 0, 100)
filepath.Walk("test", func(path string, info os.FileInfo, err error) error {
if debugging {
fmt.Println("FOUND:", path)
}
if err != nil || info.IsDir() || !testPattern.MatchString(path) {
return nil
}
if debugging {
fmt.Println("MATCHING:", path)
}
files = append(files, path)
return nil
})
return files
}
func setBash() {
findcmd := "which"
if runtime.GOOS == "windows" {
// Can't use paths returned from which even if it's on PATH in Windows
// Because our Go binary is a separate Windows app & not MinGW, it
// can't understand paths like '/usr/bin/bash', needs Windows version
findcmd = "where"
}
out, err := exec.Command(findcmd, "bash").Output()
if err != nil {
fmt.Println("Unable to find bash:", err)
os.Exit(1)
}
if len(out) == 0 {
fmt.Printf("No output from '%s bash'\n", findcmd)
os.Exit(1)
}
bashPath = strings.TrimSpace(strings.Split(string(out), "\n")[0])
if debugging {
fmt.Println("Using", bashPath)
}
// Test
_, err = exec.Command(bashPath, "--version").CombinedOutput()
if err != nil {
fmt.Println("Error calling bash:", err)
os.Exit(1)
}
}

@ -18,8 +18,6 @@ func main() {
switch *SubCommand {
case "release":
mainRelease()
case "integration":
mainIntegration()
default:
log.Fatalln("Unknown command:", *SubCommand)
}

40
t/Makefile Normal file

@ -0,0 +1,40 @@
RM ?= rm -f
PROVE ?= prove
PROVE_EXTRA_ARGS =
DEFAULT_TEST_TARGET ?= test
GO ?= go
X =
TEST_CMDS =
TEST_CMDS += ../bin/git-credential-lfsnoop$X
TEST_CMDS += ../bin/git-credential-lfstest$X
TEST_CMDS += ../bin/lfs-askpass$X
TEST_CMDS += ../bin/lfs-ssh-echo$X
TEST_CMDS += ../bin/lfs-ssh-proxy-test$X
TEST_CMDS += ../bin/lfstest-count-tests$X
TEST_CMDS += ../bin/lfstest-customadapter$X
TEST_CMDS += ../bin/lfstest-gitserver$X
TEST_CMDS += ../bin/lfstest-standalonecustomadapter$X
TEST_CMDS += ../bin/lfstest-testutils$X
all : $(DEFAULT_TEST_TARGET)
test : $(TEST_CMDS)
$(RM) -r remote test_count{,.lock}
@GIT_LFS_NO_TEST_COUNT= bash -c '. ./testenv.sh && setup'
$(PROVE) $(PROVE_EXTRA_ARGS) ./t-*.sh
@GIT_LFS_NO_TEST_COUNT= bash -c '. ./testenv.sh && shutdown'
./t-%.sh : $(TEST_CMDS)
$(RM) -r remote test_count{,.lock}
$(PROVE) -v $(PROVE_EXTRA_ARGS) $@
.PHONY : clean
clean :
$(RM) -r remote
$(RM) $(TEST_CMDS)
../bin/%$X : cmd/%.go
go build -o $@ $^

117
t/README.md Normal file

@ -0,0 +1,117 @@
# `t`
This directory contains one of the two types of tests that the Git LFS project
uses to protect against regression. The first, scattered in `*_test.go` files
throughout the repository are _unit tests_, and written in Go, designed to
uncover failures at the unit level.
The second kind--and the one contained in this directory--are _integration
tests_, which are designed to exercise Git LFS in an end-to-end fashion,
running the `git`, and `git-lfs` binaries, along with a mock Git server.
You can run all tests in this directory with any of the following:
```ShellSession
$ make
$ make test
$ make PROVE_EXTRA_ARGS=-j9 test
```
Or run a single test (for example, `t-checkout.sh`) by any of the following:
```ShellSession
$ make ./t-checkout.sh
$ make PROVE_EXTRA_ARGS=-v ./t-checkout.sh
$ ./t-checkout.sh
```
Alternatively, one can run a selection of tests (via explicitly listing them or
making use of the built-in shell globbing) by any of the following:
```ShellSession
$ make ./t-*.sh
$ make PROVE_EXTRA_ARGS=-j9 ./t-*.sh
$ ./t-*.sh
```
## Test File(s)
There are a few important kinds of files to know about in the `t` directory:
- `cmd/`: contains the source code of binaries that are useful during test
time, like the mocked Git server, or the test counting binary. For more about
the contents of this directory, see [test lifecycle](#test-lifecycle) below.
The file `t/cmd/testutils.go` is automatically linked and included during the
build process of each file in `cmd`.
- `fixtures/`: contains shell scripts that load fixture repositories useful for
testing against.
- `t-*.sh`: file(s) containing zero or more tests, typically related to
a similar topic (c.f,. `t/t-push.sh`, `t/t-pull.sh`, etc.)
- `testenv.sh`: loads environment variables useful during tests. This file is
sourced by `testlib.sh`.
- `testhelpers.sh`: loads shell functions useful during tests, like
`setup_remote_repo`, and `clone_repo`.
- `testlib.sh`: loads the `begin_test`, `end_test`, and similar functions
useful for instantiating a particular test.
## Test Lifecycle
When a test is run, the following occurs, in order:
1. Missing test binaries are compiled into the `bin` directory in the
repository root. Note: this does _not_ include the `git-lfs` binary, which
is re-compiled via `script/boostrap`.
2. An integration server is started by either (1) the `Makefile` or (2) the
`cmd/lfstest-count-test.go` program, which keeps track of the number of
running tests and starts an integration server any time the number of active
tests goes from `0` to `1`, and stops the server when it goes from `n` to
`0`.
3. After sourcing `t/testlib.sh` (& loading `t/testenv.sh`), each test is run
in sequence per file. (In other words, multiple test files can be run in
parallel, but the tests in a single file are run in sequence.)
4. An individual test will finish, and (if running under `prove`) another will
be started in its place. Once all tests are done, `t/test_count` will go to
`0`, and the test server will be torn down.
## Test Environment
There are a few environment variables that you can set to change the test suite
behavior:
* `GIT_LFS_TEST_DIR=path` - This sets the directory that is used as the current
working directory of the tests. By default, this will be in your temp dir. It's
recommended that this is set to a directory outside of any Git repository.
* `KEEPTRASH=1` - This will leave the local repository data in a `tmp` directory
and the remote repository data in `test/remote`.
Also ensure that your `noproxy` environment variable contains `127.0.0.1` host,
to allow git commands to reach the local Git server `lfstest-gitserver`.
## Writing new tests
A new test file should be named `t/t-*.sh`, where `*` is the topic of Git LFS
being tested. It should look as follows:
```bash
#!/usr/bin/env bash
. "$(dirname "$0")/testlib.sh"
begin_test "my test"
(
set -e
# ...
)
end_test
```

@ -0,0 +1,296 @@
package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
)
var (
// countFile is the path to a file (relative to the $LFSTEST_DIR) who's
// contents is the number of actively-running integration tests.
countFile = "test_count"
// lockFile is the path to a file (relative to the $LFSTEST_DIR) who's
// presence indicates that another invocation of the lfstest-count-tests
// program is modifying the test_count.
lockFile = "test_count.lock"
// lockAcquireTimeout is the maximum amount of time that we will wait
// for lockFile to become available (and thus the amount of time that we
// will wait in order to acquire the lock).
lockAcquireTimeout = 5 * time.Second
// errCouldNotAcquire indicates that the program could not acquire the
// lock needed to modify the test_count. It is a fatal error.
errCouldNotAcquire = fmt.Errorf("could not acquire lock, dying")
// errNegativeCount indicates that the count in test_count was negative,
// which is unexpected and makes this script behave in an undefined
// fashion
errNegativeCount = fmt.Errorf("unexpected negative count")
)
// countFn is a type signature that all functions who wish to modify the
// test_count must inhabit.
//
// The first and only formal parameter is the current number of running tests
// found in test_count after acquiring the lock.
//
// The returned tuple indicates (1) the new number that should be written to
// test_count, and (2) if there was an error in computing that value. If err is
// non-nil, the program will exit and test_count will not be updated.
type countFn func(int) (int, error)
func main() {
if len(os.Args) > 2 {
fmt.Fprintf(os.Stderr,
"usage: %s [increment|decrement]\n", os.Args[0])
os.Exit(1)
}
ctx, cancel := context.WithTimeout(
context.Background(), lockAcquireTimeout)
defer cancel()
if err := acquire(ctx); err != nil {
fatal(err)
}
defer release()
if len(os.Args) == 1 {
// Calling with no arguments indicates that we simply want to
// read the contents of test_count.
callWithCount(func(n int) (int, error) {
fmt.Fprintf(os.Stdout, "%d\n", n)
return n, nil
})
return
}
var err error
switch strings.ToLower(os.Args[1]) {
case "increment":
err = callWithCount(func(n int) (int, error) {
if n > 0 {
// If n>1, it is therefore true that a
// lfstest-gitserver invocation is already
// running.
//
// Hence, let's do nothing here other than
// increase the count.
return n + 1, nil
}
// The lfstest-gitserver invocation (see: below) does
// not itself create a gitserver.log in the appropriate
// directory. Thus, let's create it ourselves instead.
log, err := os.Create(fmt.Sprintf(
"%s/gitserver.log", os.Getenv("LFSTEST_DIR")))
if err != nil {
return n, err
}
// The executable name depends on the X environment
// variable, which is set in script/cibuild.
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.Command("lfstest-gitserver.exe")
} else {
cmd = exec.Command("lfstest-gitserver")
}
// The following are ported from the old
// test/testhelpers.sh, and comprise the requisite
// environment variables needed to run
// lfstest-gitserver.
cmd.Env = append(os.Environ(),
fmt.Sprintf("LFSTEST_URL=%s", os.Getenv("LFSTEST_URL")),
fmt.Sprintf("LFSTEST_SSL_URL=%s", os.Getenv("LFSTEST_SSL_URL")),
fmt.Sprintf("LFSTEST_CLIENT_CERT_URL=%s", os.Getenv("LFSTEST_CLIENT_CERT_URL")),
fmt.Sprintf("LFSTEST_DIR=%s", os.Getenv("LFSTEST_DIR")),
fmt.Sprintf("LFSTEST_CERT=%s", os.Getenv("LFSTEST_CERT")),
fmt.Sprintf("LFSTEST_CLIENT_CERT=%s", os.Getenv("LFSTEST_CLIENT_CERT")),
fmt.Sprintf("LFSTEST_CLIENT_KEY=%s", os.Getenv("LFSTEST_CLIENT_KEY")),
)
cmd.Stdout = log
// Start performs a fork/execve, hence we can abandon
// this process once it has started.
if err := cmd.Start(); err != nil {
return n, err
}
return 1, nil
})
case "decrement":
err = callWithCount(func(n int) (int, error) {
if n > 1 {
// If there is at least two tests running, we
// need not shutdown a lfstest-gitserver
// instance.
return n - 1, nil
}
// Otherwise, we need to POST to /shutdown, which will
// cause the lfstest-gitserver to abort itself.
url, err := ioutil.ReadFile(os.Getenv("LFS_URL_FILE"))
if err == nil {
_, err = http.Post(string(url)+"/shutdown",
"application/text",
strings.NewReader(time.Now().String()))
}
return 0, nil
})
}
if err != nil {
fatal(err)
}
}
var (
// acquireTick is the constant time that one tick (i.e., one attempt at
// acquiring the lock) should last.
acquireTick = 10 * time.Millisecond
)
// acquire acquires the lock file necessary to perform updates to test_count,
// and returns an error if that lock cannot be acquired.
func acquire(ctx context.Context) error {
if disabled() {
return nil
}
path, err := path(lockFile)
if err != nil {
return err
}
tick := time.NewTicker(acquireTick)
defer tick.Stop()
for {
select {
case <-tick.C:
// Try every tick of the above ticker before giving up
// and trying again.
_, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL, 0666)
if err == nil || !os.IsExist(err) {
return err
}
case <-ctx.Done():
// If the context.Context above has reached its
// deadline, we must give up.
return errCouldNotAcquire
}
}
}
// release releases the lock file so that another process can take over, or
// returns an error.
func release() error {
if disabled() {
return nil
}
path, err := path(lockFile)
if err != nil {
return err
}
return os.Remove(path)
}
// callWithCount calls the given countFn with the current count in test_count,
// and updates it with what the function returns.
//
// If the function produced an error, that will be returned instead.
func callWithCount(fn countFn) error {
path, err := path(countFile)
if err != nil {
return err
}
f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return err
}
contents, err := ioutil.ReadAll(f)
if err != nil {
return err
}
var n int = 0
if len(contents) != 0 {
n, err = strconv.Atoi(string(contents))
if err != nil {
return err
}
if n < 0 {
return errNegativeCount
}
}
after, err := fn(n)
if err != nil {
return err
}
// We want to write over the contents in the file, so "truncate" the
// file to a length of 0, and then seek to the beginning of the file to
// update the write head.
if err := f.Truncate(0); err != nil {
return err
}
if _, err := f.Seek(0, io.SeekStart); err != nil {
return err
}
if _, err := fmt.Fprintf(f, "%d", after); err != nil {
return err
}
return nil
}
// path returns an absolute path corresponding to any given path relative to the
// 't' directory of the current checkout of Git LFS.
func path(s string) (string, error) {
p := filepath.Join(filepath.Dir(os.Getenv("LFSTEST_DIR")), s)
if err := os.MkdirAll(filepath.Dir(p), 0666); err != nil {
return "", err
}
return p, nil
}
// disabled returns true if and only if the lock acquisition phase is disabled.
func disabled() bool {
s := os.Getenv("GIT_LFS_LOCK_ACQUIRE_DISABLED")
b, err := strconv.ParseBool(s)
if err != nil {
return false
}
return b
}
// fatal reports the given error (if non-nil), and then dies. If the error was
// nil, nothing happens.
func fatal(err error) {
if err == nil {
return
}
if err := release(); err != nil {
fmt.Fprintf(os.Stderr, "fatal: while dying, got: %s\n", err)
}
fmt.Fprintf(os.Stderr, "fatal: %s\n", err)
os.Exit(1)
}

@ -101,6 +101,9 @@ func main() {
mux.HandleFunc("/storage/", storageHandler)
mux.HandleFunc("/verify", verifyHandler)
mux.HandleFunc("/redirect307/", redirect307Handler)
mux.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s\n", time.Now().String())
})
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
id, ok := reqId(w)
if !ok {

@ -9,7 +9,7 @@ import (
"os"
"path/filepath"
"github.com/git-lfs/git-lfs/test"
. "github.com/git-lfs/git-lfs/t/cmd/util"
)
type TestUtilRepoCallback struct{}
@ -23,7 +23,7 @@ func (*TestUtilRepoCallback) Errorf(format string, args ...interface{}) {
}
func main() {
commandMap := map[string]func(*test.Repo){
commandMap := map[string]func(*Repo){
"addcommits": AddCommits,
}
if len(os.Args) < 2 {
@ -51,18 +51,18 @@ func main() {
os.Exit(2)
}
repo := test.WrapRepo(&TestUtilRepoCallback{}, wd)
repo := WrapRepo(&TestUtilRepoCallback{}, wd)
f(repo)
}
func AddCommits(repo *test.Repo) {
// Read stdin as JSON []*test.CommitInput
func AddCommits(repo *Repo) {
// Read stdin as JSON []*CommitInput
in, err := ioutil.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, "addcommits: Unable to read input data: %v\n", err)
os.Exit(3)
}
inputs := make([]*test.CommitInput, 0)
inputs := make([]*CommitInput, 0)
err = json.Unmarshal(in, &inputs)
if err != nil {
fmt.Fprintf(os.Stderr, "addcommits: Unable to unmarshal JSON: %v\n%v\n", string(in), err)

@ -1,4 +1,4 @@
package test
package util
// Utility functions for more complex go tests
// Need to be in a separate test package so they can be imported anywhere

@ -178,7 +178,7 @@ setup_single_local_branch_tracked_corrupt() {
#
# - Commit 'A' has 120, 140 bytes of data in a.txt, and a.md, respectively.
#
# - Commit 'B' has 30 bytes of data in a.txt, and includes commit 'A' as a
# - Commit 'B' has 30 bytes of data in a.md, and includes commit 'A' as a
# parent.
setup_multiple_local_branches() {
set -e
@ -218,6 +218,29 @@ setup_multiple_local_branches_with_gitattrs() {
git commit -m "add .gitattributes"
}
# setup_multiple_local_branches_non_standard creates a repository as follows:
#
# refs/pull/1/head
# /
# |
# B
# / \
# A refs/heads/my-feature
# |\
# | refs/heads/master
# \
# refs/pull/1/base
#
# With the same contents in 'A' and 'B' as setup_multiple_local_branches.
setup_multiple_local_branches_non_standard() {
set -e
setup_multiple_local_branches
git update-ref refs/pull/1/head "$(git rev-parse my-feature)"
git update-ref refs/pull/1/base "$(git rev-parse master)"
}
# setup_multiple_local_branches_tracked creates a repo with exactly the same
# structure as in setup_multiple_local_branches, but with all files tracked by
# Git LFS
@ -553,6 +576,28 @@ setup_local_branch_with_symlink() {
git commit -m "add symlink"
}
# setup_local_branch_with_dirty_copy creates a repository as follows:
#
# A
# \
# refs/heads/master
#
# - Commit 'A' has the contents "a.txt in a.txt, and marks a.txt as unclean
# in the working copy.
setup_local_branch_with_dirty_copy() {
set -e
reponame="migrate-single-local-branch-with-dirty-copy"
remove_and_create_local_repo "$reponame"
printf "a.txt" > a.txt
git add a.txt
git commit -m "initial commit"
printf "2" >> a.txt
}
# make_bare converts the existing full checkout of a repository into a bare one,
# and then `cd`'s into it.
make_bare() {
@ -587,4 +632,6 @@ remove_and_create_remote_repo() {
setup_remote_repo "$reponame"
clone_repo "$reponame" "$reponame"
rm clone.log
}

1
t/git-lfs-test-server-api/.gitignore vendored Normal file

@ -0,0 +1 @@
git-lfs-test-server-api*

@ -13,8 +13,8 @@ import (
"github.com/git-lfs/git-lfs/errors"
"github.com/git-lfs/git-lfs/fs"
"github.com/git-lfs/git-lfs/lfsapi"
t "github.com/git-lfs/git-lfs/t/cmd/util"
"github.com/git-lfs/git-lfs/tasklog"
"github.com/git-lfs/git-lfs/test"
"github.com/git-lfs/git-lfs/tq"
"github.com/spf13/cobra"
)
@ -64,7 +64,7 @@ func testServerApi(cmd *cobra.Command, args []string) {
// Use test repo for this to simplify the process of making sure data matches oid
// We're not performing a real test at this point (although an upload fail will break it)
var callback testDataCallback
repo := test.NewRepo(&callback)
repo := t.NewRepo(&callback)
// Force loading of config before we alter it
repo.GitEnv().All()
@ -137,7 +137,7 @@ func (*testDataCallback) Errorf(format string, args ...interface{}) {
fmt.Printf(format, args...)
}
func buildManifest(r *test.Repo) (*tq.Manifest, error) {
func buildManifest(r *t.Repo) (*tq.Manifest, error) {
// Configure the endpoint manually
finder := lfsapi.NewEndpointFinder(r)
@ -173,7 +173,7 @@ func (c *constantEndpoint) Endpoint(operation, remote string) lfsapi.Endpoint {
func (c *constantEndpoint) RemoteEndpoint(operation, remote string) lfsapi.Endpoint { return c.e }
func buildTestData(repo *test.Repo, manifest *tq.Manifest) (oidsExist, oidsMissing []TestObject, err error) {
func buildTestData(repo *t.Repo, manifest *tq.Manifest) (oidsExist, oidsMissing []TestObject, err error) {
const oidCount = 50
oidsExist = make([]TestObject, 0, oidCount)
oidsMissing = make([]TestObject, 0, oidCount)
@ -183,14 +183,14 @@ func buildTestData(repo *test.Repo, manifest *tq.Manifest) (oidsExist, oidsMissi
meter := tq.NewMeter()
meter.Logger = meter.LoggerFromEnv(repo.OSEnv())
logger.Enqueue(meter)
commit := test.CommitInput{CommitterName: "A N Other", CommitterEmail: "noone@somewhere.com"}
commit := t.CommitInput{CommitterName: "A N Other", CommitterEmail: "noone@somewhere.com"}
for i := 0; i < oidCount; i++ {
filename := fmt.Sprintf("file%d.dat", i)
sz := int64(rand.Intn(200)) + 50
commit.Files = append(commit.Files, &test.FileInput{Filename: filename, Size: sz})
commit.Files = append(commit.Files, &t.FileInput{Filename: filename, Size: sz})
meter.Add(sz)
}
outputs := repo.AddCommits([]*test.CommitInput{&commit})
outputs := repo.AddCommits([]*t.CommitInput{&commit})
// now upload
uploadQueue := tq.NewTransferQueue(tq.Upload, manifest, "origin", tq.WithProgress(meter))

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "askpass: push with GIT_ASKPASS"
(
@ -102,3 +102,33 @@ begin_test "askpass: push with SSH_ASKPASS"
grep "master -> master" push.log
)
end_test
begin_test "askpass: defaults to provided credentials"
(
set -e
reponame="askpass-provided-creds"
setup_remote_repo "$reponame"
clone_repo "$reponame" "$reponame"
git lfs track "*.dat"
echo "hello" > a.dat
git add .gitattributes a.dat
git commit -m "initial commit"
# $password is defined from test/cmd/lfstest-gitserver.go (see: skipIfBadAuth)
export LFS_ASKPASS_USERNAME="fakeuser"
export LFS_ASKPASS_PASSWORD="fakepass"
git config --local "credential.helper" ""
url=$(git config --get remote.origin.url)
newurl=${url/http:\/\//http:\/\/user\:pass@}
git remote set-url origin "$newurl"
GIT_ASKPASS="lfs-askpass" GIT_TRACE=1 GIT_CURL_VERBOSE=1 git push origin master 2>&1 | tee push.log
[ ! $(grep "filling with GIT_ASKPASS" push.log) ]
grep "master -> master" push.log
)
end_test

@ -2,7 +2,7 @@
# This is a sample Git LFS test. See test/README.md and testhelpers.sh for
# more documentation.
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "batch error handling"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "batch storage upload causes retries"
(

@ -2,7 +2,7 @@
# This is a sample Git LFS test. See test/README.md and testhelpers.sh for
# more documentation.
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "batch transfer"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "transfer queue rejects unknown OIDs"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "checkout"
(
@ -156,3 +156,29 @@ begin_test "checkout: outside git repository"
grep "Not in a git repository" checkout.log
)
end_test
begin_test "checkout: write-only file"
(
set -e
reponame="checkout-locked"
filename="a.txt"
setup_remote_repo_with_file "$reponame" "$filename"
pushd "$TRASHDIR" > /dev/null
GIT_LFS_SKIP_SMUDGE=1 clone_repo "$reponame" "${reponame}_checkout"
chmod -w "$filename"
refute_file_writeable "$filename"
assert_pointer "refs/heads/master" "$filename" "$(calc_oid "$filename\n")" 6
git lfs fetch
git lfs checkout "$filename"
refute_file_writeable "$filename"
[ "$filename" = "$(cat "$filename")" ]
popd > /dev/null
)
end_test

@ -2,7 +2,7 @@
# This is a sample Git LFS test. See test/README.md and testhelpers.sh for
# more documentation.
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "chunked transfer encoding"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
clean_setup () {
mkdir "$1"

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
ensure_git_version_isnt $VERSION_LOWER "2.15.0"

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
ensure_git_version_isnt $VERSION_LOWER "2.2.0"

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "commit, delete, then push"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "default config"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
# these tests rely on GIT_TERMINAL_PROMPT to test properly
ensure_git_version_isnt $VERSION_LOWER "2.3.0"

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
ensure_git_version_isnt $VERSION_LOWER "2.3.0"
@ -164,7 +164,7 @@ begin_test "credentials with useHttpPath, with correct password"
credcalls="$(grep "creds: git credential" push.log)"
[ "0" -eq "$(echo "$credcalls" | grep '", "")' | wc -l)" ]
expected="$(echo "$credcalls" | wc -l)"
[ "$expected" -eq "$(printf "$credcalls" | grep "test-credentials" | wc -l)" ]
[ "$expected" -eq "$(printf "$credcalls" | grep "t-credentials" | wc -l)" ]
)
end_test

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "custom-transfer-wrong-path"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "multiple revs with same OID get pushed once"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
envInitConfig='git config filter.lfs.process = "git-lfs filter-process"
git config filter.lfs.smudge = "git-lfs smudge -- %f"
@ -29,7 +29,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -81,7 +81,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -140,7 +140,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -197,7 +197,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -256,7 +256,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -316,7 +316,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -377,7 +377,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=5
TusTransfers=false
@ -445,7 +445,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -500,7 +500,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -549,7 +549,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -582,7 +582,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -615,7 +615,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -660,7 +660,7 @@ LocalWorkingDir=
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -739,7 +739,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -772,7 +772,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=false
@ -838,7 +838,7 @@ LocalWorkingDir=%s
LocalGitDir=%s
LocalGitStorageDir=%s
LocalMediaDir=%s
LocalReferenceDir=
LocalReferenceDirs=
TempDir=%s
ConcurrentTransfers=3
TusTransfers=true

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
declare -a expiration_types=("absolute" "relative" "both")

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "ext"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "http.<url>.extraHeader"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
reponame="$(basename "$0" ".sh")"
contents="big file"

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
reponame="$(basename "$0" ".sh")"
contents="a"

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
reponame="fetch-recent"

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "fetch with good ref"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
contents="a"
contents_oid=$(calc_oid "$contents")
@ -526,14 +526,14 @@ begin_test "fetch raw remote url"
git init
git lfs install --local --skip-smudge
git remote add origin $GITSERVER/test-fetch
git remote add origin "$GITSERVER/$reponame"
git pull origin master
# LFS object not downloaded, pointer in working directory
refute_local_object "$contents_oid"
grep "$content_oid" a.dat
git lfs fetch "$GITSERVER/test-fetch"
git lfs fetch "$GITSERVER/$reponame"
# LFS object downloaded, pointer still in working directory
assert_local_object "$contents_oid" 1

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "filter-branch (git-lfs/git-lfs#1773)"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
# HACK(taylor): git uses ".g<hash>" in the version name to signal that it is
# from the "next" branch, which is the only (current) version of Git that has

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "fsck default"
(

@ -2,7 +2,7 @@
# This is a sample Git LFS test. See test/README.md and testhelpers.sh for
# more documentation.
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "happy path"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
# These tests rely on behavior found in Git versions less than 2.9.0 to perform
# themselves, specifically:

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
# These tests rely on behavior found in 2.9.0 to perform themselves,
# specifically:

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "install again"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "lock with good ref"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "list a single lock with bad ref"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "logs"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "ls-files"
(
@ -334,17 +334,12 @@ begin_test "ls-files: list/stat files with escaped runes in path before commit"
(
set -e
if [ -n "$CIRCLECI" ]; then
echo >&2 "info: skipping due to known failure on CircleCI"
exit 0
fi
reponame=runes-in-path
content="zero"
checksum="d3eb539a55"
pathWithGermanRunes="german/äöü"
fileWithGermanRunes="schüüch.bin"
pathWithGermanRunes="german/äöü"
fileWithGermanRunes="schüüch.bin"
mkdir $reponame
git init "$reponame"
cd $reponame

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "malformed pointers"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "mergetool works with large files"
(

@ -1,7 +1,7 @@
#!/usr/bin/env bash
. "test/test-migrate-fixtures.sh"
. "test/testlib.sh"
. "$(dirname "$0")/fixtures/migrate.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "migrate export (default branch)"
(
@ -132,7 +132,7 @@ begin_test "migrate export (bare repository)"
md_oid="$(calc_oid "$(cat a.md)")"
txt_oid="$(calc_oid "$(cat a.txt)")"
make_bare
assert_pointer "refs/heads/master" "a.txt" "$txt_oid" "30"
@ -198,7 +198,7 @@ begin_test "migrate export (no filter)"
setup_multiple_local_branches_tracked
git lfs migrate export 2>&1 | tee migrate.log
git lfs migrate export --yes 2>&1 | tee migrate.log
if [ ${PIPESTATUS[0]} -eq 0 ]; then
echo >&2 "fatal: expected git lfs migrate export to fail, didn't"
exit 1
@ -330,7 +330,7 @@ begin_test "migrate export (include/exclude ref)"
--include="*.txt" \
--include-ref=refs/heads/my-feature \
--exclude-ref=refs/heads/master
assert_pointer "refs/heads/master" "a.md" "$md_master_oid" "21"
assert_pointer "refs/heads/master" "a.txt" "$txt_master_oid" "20"
@ -432,7 +432,8 @@ begin_test "migrate export (invalid --remote)"
setup_single_remote_branch_tracked
git lfs migrate export --include="*" --remote="zz" 2>&1 | tee migrate.log
git lfs migrate export --include="*" --remote="zz" --yes 2>&1 \
| tee migrate.log
if [ ${PIPESTATUS[0]} -eq 0 ]; then
echo >&2 "fatal: expected git lfs migrate export to fail, didn't"
exit 1

@ -1,7 +1,7 @@
#!/usr/bin/env bash
. "test/test-migrate-fixtures.sh"
. "test/testlib.sh"
. "$(dirname "$0")/fixtures/migrate.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "migrate import (--fixup)"
(
@ -11,7 +11,7 @@ begin_test "migrate import (--fixup)"
txt_oid="$(calc_oid "$(git cat-file -p :a.txt)")"
git lfs migrate import --everything --fixup
git lfs migrate import --everything --fixup --yes
assert_pointer "refs/heads/master" "a.txt" "$txt_oid" "120"
assert_local_object "$txt_oid" "120"
@ -31,7 +31,7 @@ begin_test "migrate import (--fixup, complex nested)"
a_oid="$(calc_oid "$(git cat-file -p :a.txt)")"
b_oid="$(calc_oid "$(git cat-file -p :dir/b.txt)")"
git lfs migrate import --everything --fixup
git lfs migrate import --everything --fixup --yes
assert_pointer "refs/heads/master" "a.txt" "$a_oid" "1"
refute_pointer "refs/heads/master" "b.txt"
@ -53,7 +53,7 @@ begin_test "migrate import (--fixup, --include)"
setup_single_local_branch_tracked_corrupt
git lfs migrate import --everything --fixup --include="*.txt" 2>&1 \
git lfs migrate import --everything --fixup --yes --include="*.txt" 2>&1 \
| tee migrate.log
if [ "${PIPESTATUS[0]}" -eq 0 ]; then
@ -71,7 +71,7 @@ begin_test "migrate import (--fixup, --exclude)"
setup_single_local_branch_tracked_corrupt
git lfs migrate import --everything --fixup --exclude="*.txt" 2>&1 \
git lfs migrate import --everything --fixup --yes --exclude="*.txt" 2>&1 \
| tee migrate.log
if [ "${PIPESTATUS[0]}" -eq 0 ]; then
@ -89,7 +89,7 @@ begin_test "migrate import (--fixup, --no-rewrite)"
setup_single_local_branch_tracked_corrupt
git lfs migrate import --everything --fixup --no-rewrite 2>&1 \
git lfs migrate import --everything --fixup --yes --no-rewrite 2>&1 \
| tee migrate.log
if [ "${PIPESTATUS[0]}" -eq 0 ]; then

@ -1,7 +1,7 @@
#!/usr/bin/env bash
. "test/test-migrate-fixtures.sh"
. "test/testlib.sh"
. "$(dirname "$0")/fixtures/migrate.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "migrate import --no-rewrite (default branch)"
(
@ -12,7 +12,7 @@ begin_test "migrate import --no-rewrite (default branch)"
txt_oid="$(calc_oid "$(git cat-file -p :a.txt)")"
prev_commit_oid="$(git rev-parse HEAD)"
git lfs migrate import --no-rewrite *.txt
git lfs migrate import --no-rewrite --yes *.txt
# Ensure our desired files were imported into git-lfs
assert_pointer "refs/heads/master" "a.txt" "$txt_oid" "120"
@ -46,7 +46,7 @@ begin_test "migrate import --no-rewrite (bare repository)"
txt_oid="$(calc_oid "$(git cat-file -p :a.txt)")"
md_oid="$(calc_oid "$(git cat-file -p :a.md)")"
git lfs migrate import --no-rewrite a.txt a.md
git lfs migrate import --no-rewrite --yes a.txt a.md
# Ensure our desired files were imported
assert_pointer "refs/heads/master" "a.txt" "$txt_oid" "30"
@ -78,7 +78,7 @@ begin_test "migrate import --no-rewrite (multiple branches)"
txt_oid="$(calc_oid "$(git cat-file -p :a.txt)")"
md_feature_oid="$(calc_oid "$(git cat-file -p my-feature:a.md)")"
git lfs migrate import --no-rewrite *.txt *.md
git lfs migrate import --no-rewrite --yes *.txt *.md
# Ensure our desired files were imported
assert_pointer "refs/heads/master" "a.md" "$md_oid" "140"
@ -111,7 +111,7 @@ begin_test "migrate import --no-rewrite (no .gitattributes)"
setup_multiple_local_branches
# Ensure command fails if no .gitattributes files are present
git lfs migrate import --no-rewrite *.txt *.md 2>&1 | tee migrate.log
git lfs migrate import --no-rewrite --yes *.txt *.md 2>&1 | tee migrate.log
if [ ${PIPESTATUS[0]} -eq 0 ]; then
echo >&2 "fatal: expected git lfs migrate import --no-rewrite to fail, didn't"
exit 1
@ -139,7 +139,7 @@ begin_test "migrate import --no-rewrite (nested .gitattributes)"
nested_md_oid="$(calc_oid "$(git cat-file -p :b/a.md)")"
txt_oid="$(calc_oid "$(git cat-file -p :a.txt)")"
git lfs migrate import --no-rewrite a.txt b/a.md
git lfs migrate import --no-rewrite --yes a.txt b/a.md
# Ensure a.txt and subtree/a.md were imported, even though *.md only exists in the
# nested subtree/.gitattributes file
@ -152,7 +152,7 @@ begin_test "migrate import --no-rewrite (nested .gitattributes)"
# Failure should occur when trying to import a.md as no entry exists in
# top-level .gitattributes file
git lfs migrate import --no-rewrite a.md 2>&1 | tee migrate.log
git lfs migrate import --no-rewrite --yes a.md 2>&1 | tee migrate.log
if [ ${PIPESTATUS[0]} -eq 0 ]; then
echo >&2 "fatal: expected git lfs migrate import --no-rewrite to fail, didn't"
exit 1
@ -171,7 +171,7 @@ begin_test "migrate import --no-rewrite (with commit message)"
prev_commit_oid="$(git rev-parse HEAD)"
expected_commit_msg="run git-lfs migrate import --no-rewrite"
git lfs migrate import --message "$expected_commit_msg" --no-rewrite *.txt
git lfs migrate import --message "$expected_commit_msg" --no-rewrite --yes *.txt
# Ensure the git history remained the same
new_commit_oid="$(git rev-parse HEAD~1)"
@ -201,7 +201,7 @@ begin_test "migrate import --no-rewrite (with empty commit message)"
prev_commit_oid="$(git rev-parse HEAD)"
git lfs migrate import -m "" --no-rewrite *.txt
git lfs migrate import -m "" --no-rewrite --yes *.txt
# Ensure the git history remained the same
new_commit_oid="$(git rev-parse HEAD~1)"

@ -1,7 +1,7 @@
#!/usr/bin/env bash
. "test/test-migrate-fixtures.sh"
. "test/testlib.sh"
. "$(dirname "$0")/fixtures/migrate.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "migrate import (default branch)"
(
@ -417,7 +417,7 @@ begin_test "migrate import (existing .gitattributes)"
txt_master_oid="$(calc_oid "$(git cat-file -p "$master:a.txt")")"
git lfs migrate import --include-ref=refs/heads/master --include="*.txt"
git lfs migrate import --yes --include-ref=refs/heads/master --include="*.txt"
assert_local_object "$txt_master_oid" "120"
@ -707,3 +707,85 @@ begin_test "migrate import (multiple remotes)"
assert_ref_unmoved "master" "$original_master" "$migrated_master"
)
end_test
begin_test "migrate import (dirty copy, negative answer)"
(
set -e
setup_local_branch_with_dirty_copy
original_master="$(git rev-parse master)"
echo "n" | git lfs migrate import --everything 2>&1 | tee migrate.log
grep "migrate: working copy must not be dirty" migrate.log
migrated_master="$(git rev-parse master)"
assert_ref_unmoved "master" "$original_master" "$migrated_master"
)
end_test
begin_test "migrate import (dirty copy, unknown then negative answer)"
(
set -e
setup_local_branch_with_dirty_copy
original_master="$(git rev-parse master)"
echo "x\nn" | git lfs migrate import --everything 2>&1 | tee migrate.log
cat migrate.log
[ "2" -eq "$(grep -o "override changes in your working copy" migrate.log \
| wc -l | awk '{ print $1 }')" ]
grep "migrate: working copy must not be dirty" migrate.log
migrated_master="$(git rev-parse master)"
assert_ref_unmoved "master" "$original_master" "$migrated_master"
)
end_test
begin_test "migrate import (dirty copy, positive answer)"
(
set -e
setup_local_branch_with_dirty_copy
oid="$(calc_oid "$(git cat-file -p :a.txt)")"
echo "y" | git lfs migrate import --everything 2>&1 | tee migrate.log
grep "migrate: changes in your working copy will be overridden ..." \
migrate.log
assert_pointer "refs/heads/master" "a.txt" "$oid" "5"
assert_local_object "$oid" "5"
)
end_test
begin_test "migrate import (non-standard refs)"
(
set -e
setup_multiple_local_branches_non_standard
md_oid="$(calc_oid "$(git cat-file -p :a.md)")"
txt_oid="$(calc_oid "$(git cat-file -p :a.txt)")"
md_feature_oid="$(calc_oid "$(git cat-file -p my-feature:a.md)")"
git lfs migrate import --everything
assert_pointer "refs/heads/master" "a.md" "$md_oid" "140"
assert_pointer "refs/heads/master" "a.txt" "$txt_oid" "120"
assert_pointer "refs/pull/1/base" "a.md" "$md_oid" "140"
assert_pointer "refs/pull/1/base" "a.txt" "$txt_oid" "120"
assert_pointer "refs/heads/my-feature" "a.txt" "$txt_oid" "120"
assert_pointer "refs/pull/1/head" "a.txt" "$txt_oid" "120"
assert_local_object "$md_oid" "140"
assert_local_object "$txt_oid" "120"
assert_local_object "$md_feature_oid" "30"
)
end_test

@ -1,7 +1,7 @@
#!/usr/bin/env bash
. "test/test-migrate-fixtures.sh"
. "test/testlib.sh"
. "$(dirname "$0")/fixtures/migrate.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "migrate info (default branch)"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
# these tests rely on GIT_TERMINAL_PROMPT to test properly
ensure_git_version_isnt $VERSION_LOWER "2.3.0"

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "pointer --file --stdin"
@ -110,6 +110,7 @@ begin_test "pointer --stdin without stdin"
[ "1" = "$status" ]
)
end_test
begin_test "pointer --stdin with bad pointer"
(
@ -126,6 +127,7 @@ Pointer file error: invalid header"
[ "1" = "$status" ]
)
end_test
begin_test "pointer --file --pointer mismatch"
(
@ -239,7 +241,7 @@ begin_test "pointer invalid --pointer"
expected="Pointer from some-pointer
Pointer file error: invalid header
Pointer file error: invalid header"
diff -u <(printf "$expected") <(printf "$output")

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "post-checkout"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "post-commit"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "post-merge"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "pre-push with good ref"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "progress meter displays positive progress"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
reponame="$(basename "$0" ".sh")"

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
ensure_git_version_isnt $VERSION_LOWER "2.5.0"

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "prune unreferenced and old"
(

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "pull"
(
@ -149,7 +149,7 @@ begin_test "pull without clean filter"
(
set -e
GIT_LFS_SKIP_SMUDGE=1 git clone $GITSERVER/test-pull no-clean
GIT_LFS_SKIP_SMUDGE=1 git clone $GITSERVER/t-pull no-clean
cd no-clean
git lfs uninstall
git config --list > config.txt
@ -198,7 +198,7 @@ begin_test "pull with raw remote url"
git init
git lfs install --local --skip-smudge
git remote add origin $GITSERVER/test-pull
git remote add origin $GITSERVER/t-pull
git pull origin master
contents="a"
@ -208,7 +208,7 @@ begin_test "pull with raw remote url"
refute_local_object "$contents_oid"
grep "$contents_oid" a.dat
git lfs pull "$GITSERVER/test-pull"
git lfs pull "$GITSERVER/t-pull"
echo "pulled!"
# LFS object downloaded and in working directory

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
ensure_git_version_isnt $VERSION_LOWER "2.3.0"

@ -1,6 +1,6 @@
#!/usr/bin/env bash
. "test/testlib.sh"
. "$(dirname "$0")/testlib.sh"
begin_test "push with missing objects (lfs.allowincompletepush true)"
(

Some files were not shown because too many files have changed in this diff Show More