Merge branch 'master' into ttaylorr/migrate-warn-if-dirty
This commit is contained in:
commit
9763ac1623
@ -179,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 {
|
||||
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()
|
||||
|
@ -311,8 +311,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 +346,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,15 +253,19 @@ 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" \
|
||||
$ git lfs migrate import --no-rewrite -m "Import .zip, .mp3, .psd files" \
|
||||
test.zip *.mpd *.psd
|
||||
```
|
||||
|
||||
|
100
fs/fs.go
100
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
|
||||
@ -25,7 +35,7 @@ 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)
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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 ""
|
||||
}
|
||||
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)
|
||||
|
@ -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)
|
||||
|
16
lfs/lfs.go
16
lfs/lfs.go
@ -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
|
||||
}
|
||||
for _, altMediafile := range altMediafiles {
|
||||
tracerx.Printf("altMediafile: %s", altMediafile)
|
||||
if altMediafile != "" && tools.FileExistsOfSize(altMediafile, size) {
|
||||
return LinkOrCopy(cfg, altMediafile, mediafile)
|
||||
err = LinkOrCopy(cfg, altMediafile, mediafile)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
106
lfsapi/creds.go
106
lfsapi/creds.go
@ -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
|
||||
|
143
test/test-alternates.sh
Executable file
143
test/test-alternates.sh
Executable file
@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
. "test/testlib.sh"
|
||||
|
||||
begin_test "alternates (single)"
|
||||
(
|
||||
set -e
|
||||
|
||||
reponame="alternates-single-alternate"
|
||||
setup_remote_repo_with_file "$reponame" "a.txt"
|
||||
|
||||
pushd "$TRASHDIR" > /dev/null
|
||||
clone_repo "$reponame" "${reponame}_alternate"
|
||||
popd > /dev/null
|
||||
|
||||
rm -rf .git/lfs/objects
|
||||
|
||||
alternate="$TRASHDIR/${reponame}_alternate/.git/objects"
|
||||
echo "$alternate" > .git/objects/info/alternates
|
||||
|
||||
GIT_TRACE=1 git lfs fetch origin master 2>&1 | tee fetch.log
|
||||
[ "0" -eq "$(grep -c "sending batch of size 1" fetch.log)" ]
|
||||
)
|
||||
end_test
|
||||
|
||||
begin_test "alternates (multiple)"
|
||||
(
|
||||
set -e
|
||||
|
||||
reponame="alternates-multiple-alternates"
|
||||
setup_remote_repo_with_file "$reponame" "a.txt"
|
||||
|
||||
pushd "$TRASHDIR" > /dev/null
|
||||
clone_repo "$reponame" "${reponame}_alternate_stale"
|
||||
rm -rf .git/lfs/objects
|
||||
popd > /dev/null
|
||||
pushd "$TRASHDIR" > /dev/null
|
||||
clone_repo "$reponame" "${reponame}_alternate"
|
||||
popd > /dev/null
|
||||
|
||||
rm -rf .git/lfs/objects
|
||||
|
||||
alternate_stale="$TRASHDIR/${reponame}_alternate_stale/.git/objects"
|
||||
alternate="$TRASHDIR/${reponame}_alternate/.git/objects"
|
||||
echo "$alternate" > .git/objects/info/alternates
|
||||
echo "$alternate_stale" >> .git/objects/info/alternates
|
||||
|
||||
GIT_TRACE=1 git lfs fetch origin master 2>&1 | tee fetch.log
|
||||
[ "0" -eq "$(grep -c "sending batch of size 1" fetch.log)" ]
|
||||
)
|
||||
end_test
|
||||
|
||||
begin_test "alternates (commented)"
|
||||
(
|
||||
set -e
|
||||
|
||||
reponame="alternates-commented-alternate"
|
||||
setup_remote_repo_with_file "$reponame" "a.txt"
|
||||
|
||||
pushd "$TRASHDIR" > /dev/null
|
||||
clone_repo "$reponame" "${reponame}_alternate"
|
||||
popd > /dev/null
|
||||
|
||||
rm -rf .git/lfs/objects
|
||||
|
||||
alternate="$TRASHDIR/${reponame}_alternate/.git/objects"
|
||||
echo "# $alternate" > .git/objects/info/alternates
|
||||
|
||||
GIT_TRACE=1 git lfs fetch origin master 2>&1 | tee fetch.log
|
||||
[ "1" -eq "$(grep -c "sending batch of size 1" fetch.log)" ]
|
||||
)
|
||||
end_test
|
||||
|
||||
begin_test "alternates (quoted)"
|
||||
(
|
||||
set -e
|
||||
|
||||
reponame="alternates-quoted-alternate"
|
||||
setup_remote_repo_with_file "$reponame" "a.txt"
|
||||
|
||||
pushd "$TRASHDIR" > /dev/null
|
||||
clone_repo "$reponame" "${reponame}_alternate"
|
||||
popd > /dev/null
|
||||
|
||||
rm -rf .git/lfs/objects
|
||||
|
||||
alternate="$TRASHDIR/${reponame}_alternate/.git/objects"
|
||||
echo "\"$alternate\"" > .git/objects/info/alternates
|
||||
|
||||
GIT_TRACE=1 git lfs fetch origin master 2>&1 | tee fetch.log
|
||||
[ "0" -eq "$(grep -c "sending batch of size 1" fetch.log)" ]
|
||||
)
|
||||
end_test
|
||||
|
||||
begin_test "alternates (OS environment, single)"
|
||||
(
|
||||
set -e
|
||||
|
||||
reponame="alternates-environment-single-alternate"
|
||||
setup_remote_repo_with_file "$reponame" "a.txt"
|
||||
|
||||
pushd "$TRASHDIR" > /dev/null
|
||||
clone_repo "$reponame" "${reponame}_alternate"
|
||||
popd > /dev/null
|
||||
|
||||
rm -rf .git/lfs/objects
|
||||
|
||||
alternate="$TRASHDIR/${reponame}_alternate/.git/objects"
|
||||
|
||||
GIT_ALTERNATE_OBJECT_DIRECTORIES="$alternate" \
|
||||
GIT_TRACE=1 \
|
||||
git lfs fetch origin master 2>&1 | tee fetch.log
|
||||
[ "0" -eq "$(grep -c "sending batch of size 1" fetch.log)" ]
|
||||
)
|
||||
end_test
|
||||
|
||||
begin_test "alternates (OS environment, multiple)"
|
||||
(
|
||||
set -e
|
||||
|
||||
reponame="alternates-environment-multiple-alternates"
|
||||
setup_remote_repo_with_file "$reponame" "a.txt"
|
||||
|
||||
pushd "$TRASHDIR" > /dev/null
|
||||
clone_repo "$reponame" "${reponame}_alternate_stale"
|
||||
rm -rf .git/lfs/objects
|
||||
popd > /dev/null
|
||||
pushd "$TRASHDIR" > /dev/null
|
||||
clone_repo "$reponame" "${reponame}_alternate"
|
||||
popd > /dev/null
|
||||
|
||||
rm -rf .git/lfs/objects
|
||||
|
||||
alternate_stale="$TRASHDIR/${reponame}_alternate_stale/.git/objects"
|
||||
alternate="$TRASHDIR/${reponame}_alternate/.git/objects"
|
||||
sep="$(native_path_list_separator)"
|
||||
|
||||
GIT_ALTERNATE_OBJECT_DIRECTORIES="$alternate_stale$sep$alternate" \
|
||||
GIT_TRACE=1 \
|
||||
git lfs fetch origin master 2>&1 | tee fetch.log
|
||||
[ "0" -eq "$(grep -c "sending batch of size 1" fetch.log)" ]
|
||||
)
|
||||
end_test
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -763,3 +763,29 @@ begin_test "migrate import (dirty copy, positive answer)"
|
||||
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
|
||||
|
@ -25,7 +25,7 @@ LocalWorkingDir=$(native_path_escaped "$TRASHDIR/$reponame")
|
||||
LocalGitDir=$(native_path_escaped "$TRASHDIR/$reponame/.git")
|
||||
LocalGitStorageDir=$(native_path_escaped "$TRASHDIR/$reponame/.git")
|
||||
LocalMediaDir=$(native_path_escaped "$TRASHDIR/$reponame/.git/lfs/objects")
|
||||
LocalReferenceDir=
|
||||
LocalReferenceDirs=
|
||||
TempDir=$(native_path_escaped "$TRASHDIR/$reponame/.git/lfs/tmp")
|
||||
ConcurrentTransfers=3
|
||||
TusTransfers=false
|
||||
@ -61,7 +61,7 @@ LocalWorkingDir=$(native_path_escaped "$TRASHDIR/$worktreename")
|
||||
LocalGitDir=$(native_path_escaped "$TRASHDIR/$reponame/.git/worktrees/$worktreename")
|
||||
LocalGitStorageDir=$(native_path_escaped "$TRASHDIR/$reponame/.git")
|
||||
LocalMediaDir=$(native_path_escaped "$TRASHDIR/$reponame/.git/lfs/objects")
|
||||
LocalReferenceDir=
|
||||
LocalReferenceDirs=
|
||||
TempDir=$(native_path_escaped "$TRASHDIR/$reponame/.git/worktrees/$worktreename/lfs/tmp")
|
||||
ConcurrentTransfers=3
|
||||
TusTransfers=false
|
||||
|
@ -40,7 +40,7 @@ refute_pointer() {
|
||||
|
||||
file=$(git cat-file -p $gitblob)
|
||||
version="version https://git-lfs.github.com/spec/v[0-9]"
|
||||
oid="oid sha256:[0-9a-f]\{32\}"
|
||||
oid="oid sha256:[0-9a-f]\{64\}"
|
||||
size="size [0-9]*"
|
||||
regex="$version.*$oid.*$size"
|
||||
|
||||
@ -730,6 +730,16 @@ native_path_escaped() {
|
||||
escape_path "$unescaped"
|
||||
}
|
||||
|
||||
# native_path_list_separator prints the operating system-specific path list
|
||||
# separator.
|
||||
native_path_list_separator() {
|
||||
if [ "$IS_WINDOWS" -eq 1 ]; then
|
||||
printf ";";
|
||||
else
|
||||
printf ":";
|
||||
fi
|
||||
}
|
||||
|
||||
cat_end() {
|
||||
if [ $IS_WINDOWS -eq 1 ]; then
|
||||
printf '^M$'
|
||||
|
Loading…
Reference in New Issue
Block a user