diff -Nru git-lfs-2.10.0/CHANGELOG.md git-lfs-2.11.0/CHANGELOG.md --- git-lfs-2.10.0/CHANGELOG.md 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/CHANGELOG.md 2020-05-08 16:06:35.000000000 +0000 @@ -1,5 +1,59 @@ # Git LFS Changelog +## 2.11.0 (8 May 2020) + +This release introduces several new features, such as better support for unnamed +local paths and URLs as remotes, support for `submodule.recurse`, exponential +backoff on failure, and support for renegotiation. In addition, numerous bugs +have been fixed and miscellaneous issues have been addressed. + +We would like to extend a special thanks to the following open-source +contributors: + +* @bluekeyes for adding support for exponential backoff +* @pluehne for adding support for `submodule.recurse` +* @Electric26 for fixing the default behavior of a prompt +* @nataliechen1 for fixing certain upload retry failures +* @shalashik for fixing a panic during cherry-pick +* @swisspol for updating our documentation to reflect supported `.lfsconfig` + keys +* @dan2468 for updating the copyright year + +### Features + +* Allow literal local paths as remotes #4119 (@bk2204) +* pre-push: find named remote for URL if possible #4103 (@bk2204) +* tq: add exponential backoff for retries #4097 (@bluekeyes) +* migrate import: set text to unspecified for excluded fields #4068 (@bk2204) +* Update list of distros for packagecloud.io #4080 (@bk2204) +* lfshttp: allow renegotiation #4066 (@bk2204) +* Support submodule.recurse = true #4063 (@pluehne) +* add man page for the post-commit hook command #4052 (@chrisd8088) +* Add an option to control warning about files larger than 4 GiB #4009 (@bk2204) + +### Bugs + +* commands/command_migrate.go: fix bug #4116 (@Electric26) +* git: avoid "bad object" messages when force-pushing #4102 (@bk2204) +* git: avoid trying to rewrite remote tags as remote branches #4096 (@bk2204) +* make Go tests run consistently using local binary #4084 (@chrisd8088) +* commands: don't honor lfs.fetch* for ls-files #4083 (@bk2204) +* commands: print help output with --help #4059 (@bk2204) +* fail dedup command with explanation when LFS extensions configured #4045 (@chrisd8088) +* fix upload retry 'file already closed' issue' #4042 (@nataliechen1) +* commands/command_filter_process: cherry-pick of several commits cause panic error #4017 (@shalashik) +* Check error when creating local storage directory #4016 (@bk2204) +* track: detect duplicate patterns with --filename #4000 (@bk2204) + +### Misc + +* Removed lfs.extension.* from list of supported keys for .lfsconfig #4044 (@swisspol) +* Tidy modules #4035 (@bk2204) +* README: explain how to verify releases #4022 (@bk2204) +* docs: document git lfs migrate --yes #4023 (@bk2204) +* Stop using cgo on amd64 Linux #4026 (@bk2204) +* updated copyright year #3995 (@dan2468) + ## 2.10.0 (21 January 2020) This release introduces several new features, such as support for local paths in diff -Nru git-lfs-2.10.0/commands/command_clone.go git-lfs-2.11.0/commands/command_clone.go --- git-lfs-2.10.0/commands/command_clone.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_clone.go 2020-05-08 16:06:35.000000000 +0000 @@ -79,7 +79,7 @@ if ref, err := git.CurrentRef(); err == nil { includeArg, excludeArg := getIncludeExcludeArgs(cmd) - filter := buildFilepathFilter(cfg, includeArg, excludeArg) + filter := buildFilepathFilter(cfg, includeArg, excludeArg, true) if cloneFlags.NoCheckout || cloneFlags.Bare { // If --no-checkout or --bare then we shouldn't check out, just fetch instead fetchRef(ref.Name, filter) diff -Nru git-lfs-2.10.0/commands/command_dedup.go git-lfs-2.11.0/commands/command_dedup.go --- git-lfs-2.10.0/commands/command_dedup.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_dedup.go 2020-05-08 16:06:35.000000000 +0000 @@ -33,6 +33,10 @@ Exit("This system does not support deduplication. %s", err) } + if len(cfg.Extensions()) > 0 { + Exit("This platform supports file de-duplication, however, Git LFS extensions are configured and therefore de-duplication can not be used.") + } + Print("OK: This platform and repository support file de-duplication.") } @@ -49,6 +53,10 @@ Exit("This system does not support deduplication.") } + if len(cfg.Extensions()) > 0 { + Exit("This platform supports file de-duplication, however, Git LFS extensions are configured and therefore de-duplication can not be used.") + } + if dirty, err := git.IsWorkingCopyDirty(); err != nil { ExitWithError(err) } else if dirty { diff -Nru git-lfs-2.10.0/commands/command_fetch.go git-lfs-2.11.0/commands/command_fetch.go --- git-lfs-2.10.0/commands/command_fetch.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_fetch.go 2020-05-08 16:06:35.000000000 +0000 @@ -88,7 +88,7 @@ } } else { // !all - filter := buildFilepathFilter(cfg, include, exclude) + filter := buildFilepathFilter(cfg, include, exclude, true) // Fetch refs sequentially per arg order; duplicates in later refs will be ignored for _, ref := range refs { diff -Nru git-lfs-2.10.0/commands/command_filter_process.go git-lfs-2.11.0/commands/command_filter_process.go --- git-lfs-2.10.0/commands/command_filter_process.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_filter_process.go 2020-05-08 16:06:35.000000000 +0000 @@ -63,21 +63,10 @@ ptrs := make(map[string]*lfs.Pointer) var q *tq.TransferQueue - closeOnce := new(sync.Once) - available := make(chan *tq.Transfer) - - if supportsDelay { - q = tq.NewTransferQueue( - tq.Download, - getTransferManifestOperationRemote("download", cfg.Remote()), - cfg.Remote(), - tq.RemoteRef(currentRemoteRef()), - ) - go infiniteTransferBuffer(q, available) - } - var malformed []string var malformedOnWindows []string + var closeOnce *sync.Once + var available chan *tq.Transfer gitfilter := lfs.NewGitFilter(cfg) for s.Scan() { var n int64 @@ -99,6 +88,19 @@ n = ptr.Size } case "smudge": + if q == nil && supportsDelay { + closeOnce = new(sync.Once) + available = make(chan *tq.Transfer) + + q = tq.NewTransferQueue( + tq.Download, + getTransferManifestOperationRemote("download", cfg.Remote()), + cfg.Remote(), + tq.RemoteRef(currentRemoteRef()), + ) + go infiniteTransferBuffer(q, available) + } + w = git.NewPktlineWriter(os.Stdout, smudgeFilterBufferCapacity) if req.Header["can-delay"] == "1" { var ptr *lfs.Pointer @@ -124,8 +126,8 @@ closeOnce.Do(func() { // The first time that Git sends us the // 'list_available_blobs' command, it is given - // that no more smudge commands will be issued - // with _new_ checkout entries. + // that now it waiting until all delayed blobs + // are available within this smudge filter call // // This means that, by the time that we're here, // we have seen all entries in the checkout, and @@ -158,6 +160,12 @@ // accept it later. paths = append(paths, fmt.Sprintf("pathname=%s", path)) } + // At this point all items have been completely processed, + // so we explicitly close transfer queue. If Git issues + // another `smudge` command the transfer queue will be + // created from scratch. Transfer queue needs to be recreated + // because it has been already partially closed by `q.Wait()` + q = nil } err = s.WriteList(paths) default: @@ -201,7 +209,7 @@ } } - if len(malformedOnWindows) > 0 { + if len(malformedOnWindows) > 0 && cfg.Git.Bool("lfs.largefilewarning", true) { fmt.Fprintf(os.Stderr, "Encountered %d file(s) that may not have been copied correctly on Windows:\n", len(malformedOnWindows)) for _, m := range malformedOnWindows { diff -Nru git-lfs-2.10.0/commands/command_ls_files.go git-lfs-2.11.0/commands/command_ls_files.go --- git-lfs-2.10.0/commands/command_ls_files.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_ls_files.go 2020-05-08 16:06:35.000000000 +0000 @@ -107,7 +107,7 @@ defer gitscanner.Close() includeArg, excludeArg := getIncludeExcludeArgs(cmd) - gitscanner.Filter = buildFilepathFilter(cfg, includeArg, excludeArg) + gitscanner.Filter = buildFilepathFilter(cfg, includeArg, excludeArg, false) if len(args) == 0 { // Only scan the index when "git lfs ls-files" was invoked with diff -Nru git-lfs-2.10.0/commands/command_migrate_export.go git-lfs-2.11.0/commands/command_migrate_export.go --- git-lfs-2.10.0/commands/command_migrate_export.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_migrate_export.go 2020-05-08 16:06:35.000000000 +0000 @@ -136,7 +136,7 @@ } if _, err := os.Stat(downloadPath); os.IsNotExist(err) { - q.Add(p.Name, downloadPath, p.Oid, p.Size, false) + q.Add(p.Name, downloadPath, p.Oid, p.Size, false, nil) } }) gs.ScanRefs(opts.Include, opts.Exclude, nil) diff -Nru git-lfs-2.10.0/commands/command_migrate.go git-lfs-2.11.0/commands/command_migrate.go --- git-lfs-2.10.0/commands/command_migrate.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_migrate.go 2020-05-08 16:06:35.000000000 +0000 @@ -303,7 +303,7 @@ // filter given by the --include and --exclude arguments. func getHistoryRewriter(cmd *cobra.Command, db *gitobj.ObjectDatabase, l *tasklog.Logger) *githistory.Rewriter { include, exclude := getIncludeExcludeArgs(cmd) - filter := buildFilepathFilter(cfg, include, exclude) + filter := buildFilepathFilter(cfg, include, exclude, false) return githistory.NewRewriter(db, githistory.WithFilter(filter), githistory.WithLogger(l)) @@ -341,7 +341,7 @@ case "n", "N": proceed = false break L - case "y", "Y": + case "y", "Y", "": proceed = true break L } diff -Nru git-lfs-2.10.0/commands/command_migrate_import.go git-lfs-2.11.0/commands/command_migrate_import.go --- git-lfs-2.10.0/commands/command_migrate_import.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_migrate_import.go 2020-05-08 16:06:35.000000000 +0000 @@ -283,7 +283,7 @@ } for _, exclude := range filter.Exclude() { - tracked.Add(fmt.Sprintf("%s text -filter -merge -diff", escapeAttrPattern(exclude))) + tracked.Add(fmt.Sprintf("%s !text -filter -merge -diff", escapeAttrPattern(exclude))) } return tracked diff -Nru git-lfs-2.10.0/commands/command_pre_push.go git-lfs-2.11.0/commands/command_pre_push.go --- git-lfs-2.10.0/commands/command_pre_push.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_pre_push.go 2020-05-08 16:06:35.000000000 +0000 @@ -47,7 +47,8 @@ requireGitVersion() // Remote is first arg - if err := cfg.SetValidPushRemote(args[0]); err != nil { + remote, _ := git.MapRemoteURL(args[0], true) + if err := cfg.SetValidPushRemote(remote); err != nil { Exit("Invalid remote name %q: %s", args[0], err) } diff -Nru git-lfs-2.10.0/commands/command_pull.go git-lfs-2.11.0/commands/command_pull.go --- git-lfs-2.10.0/commands/command_pull.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_pull.go 2020-05-08 16:06:35.000000000 +0000 @@ -27,7 +27,7 @@ } includeArg, excludeArg := getIncludeExcludeArgs(cmd) - filter := buildFilepathFilter(cfg, includeArg, excludeArg) + filter := buildFilepathFilter(cfg, includeArg, excludeArg, true) pull(filter) } diff -Nru git-lfs-2.10.0/commands/commands.go git-lfs-2.11.0/commands/commands.go --- git-lfs-2.10.0/commands/commands.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/commands.go 2020-05-08 16:06:35.000000000 +0000 @@ -126,14 +126,14 @@ return git.NewRefUpdate(cfg.Git, cfg.PushRemote(), cfg.CurrentRef(), nil).Right() } -func buildFilepathFilter(config *config.Configuration, includeArg, excludeArg *string) *filepathfilter.Filter { - inc, exc := determineIncludeExcludePaths(config, includeArg, excludeArg) +func buildFilepathFilter(config *config.Configuration, includeArg, excludeArg *string, useFetchOptions bool) *filepathfilter.Filter { + inc, exc := determineIncludeExcludePaths(config, includeArg, excludeArg, useFetchOptions) return filepathfilter.New(inc, exc) } -func downloadTransfer(p *lfs.WrappedPointer) (name, path, oid string, size int64, missing bool) { - path, _ = cfg.Filesystem().ObjectPath(p.Oid) - return p.Name, path, p.Oid, p.Size, false +func downloadTransfer(p *lfs.WrappedPointer) (name, path, oid string, size int64, missing bool, err error) { + path, err = cfg.Filesystem().ObjectPath(p.Oid) + return p.Name, path, p.Oid, p.Size, false, err } // Get user-readable manual install steps for hooks @@ -449,14 +449,22 @@ } } -func determineIncludeExcludePaths(config *config.Configuration, includeArg, excludeArg *string) (include, exclude []string) { +func determineIncludeExcludePaths(config *config.Configuration, includeArg, excludeArg *string, useFetchOptions bool) (include, exclude []string) { if includeArg == nil { - include = config.FetchIncludePaths() + if useFetchOptions { + include = config.FetchIncludePaths() + } else { + include = []string{} + } } else { include = tools.CleanPaths(*includeArg, ",") } if excludeArg == nil { - exclude = config.FetchExcludePaths() + if useFetchOptions { + exclude = config.FetchExcludePaths() + } else { + exclude = []string{} + } } else { exclude = tools.CleanPaths(*excludeArg, ",") } diff -Nru git-lfs-2.10.0/commands/command_smudge.go git-lfs-2.11.0/commands/command_smudge.go --- git-lfs-2.10.0/commands/command_smudge.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_smudge.go 2020-05-08 16:06:35.000000000 +0000 @@ -59,7 +59,7 @@ if !skip && filter.Allows(filename) { if _, statErr := os.Stat(path); statErr != nil { - q.Add(filename, path, ptr.Oid, ptr.Size, false) + q.Add(filename, path, ptr.Oid, ptr.Size, false, err) return 0, true, ptr, nil } diff -Nru git-lfs-2.10.0/commands/commands_test.go git-lfs-2.11.0/commands/commands_test.go --- git-lfs-2.10.0/commands/commands_test.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/commands_test.go 2020-05-08 16:06:35.000000000 +0000 @@ -19,7 +19,7 @@ func TestDetermineIncludeExcludePathsReturnsCleanedPaths(t *testing.T) { inc := "/some/include" exc := "/some/exclude" - i, e := determineIncludeExcludePaths(testcfg, &inc, &exc) + i, e := determineIncludeExcludePaths(testcfg, &inc, &exc, true) assert.Equal(t, []string{"/some/include"}, i) assert.Equal(t, []string{"/some/exclude"}, e) @@ -28,15 +28,22 @@ func TestDetermineIncludeExcludePathsReturnsEmptyPaths(t *testing.T) { inc := "" exc := "" - i, e := determineIncludeExcludePaths(testcfg, &inc, &exc) + i, e := determineIncludeExcludePaths(testcfg, &inc, &exc, true) assert.Empty(t, i) assert.Empty(t, e) } func TestDetermineIncludeExcludePathsReturnsDefaultsWhenAbsent(t *testing.T) { - i, e := determineIncludeExcludePaths(testcfg, nil, nil) + i, e := determineIncludeExcludePaths(testcfg, nil, nil, true) assert.Equal(t, []string{"/default/include"}, i) assert.Equal(t, []string{"/default/exclude"}, e) } + +func TestDetermineIncludeExcludePathsReturnsNothingWhenAbsent(t *testing.T) { + i, e := determineIncludeExcludePaths(testcfg, nil, nil, false) + + assert.Empty(t, i) + assert.Empty(t, e) +} diff -Nru git-lfs-2.10.0/commands/command_track.go git-lfs-2.11.0/commands/command_track.go --- git-lfs-2.10.0/commands/command_track.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/command_track.go 2020-05-08 16:06:35.000000000 +0000 @@ -77,6 +77,16 @@ ArgsLoop: for _, unsanitizedPattern := range args { pattern := trimCurrentPrefix(cleanRootPath(unsanitizedPattern)) + + // Generate the new / changed attrib line for merging + var encodedArg string + if trackFilenameFlag { + encodedArg = escapeGlobCharacters(pattern) + pattern = escapeGlobCharacters(pattern) + } else { + encodedArg = escapeAttrPattern(pattern) + } + if !trackNoModifyAttrsFlag { for _, known := range knownPatterns { if unescapeAttrPattern(known.Path) == filepath.Join(relpath, pattern) && @@ -89,15 +99,6 @@ } } - // Generate the new / changed attrib line for merging - var encodedArg string - if trackFilenameFlag { - encodedArg = escapeGlobCharacters(pattern) - pattern = escapeGlobCharacters(pattern) - } else { - encodedArg = escapeAttrPattern(pattern) - } - lockableArg := "" if trackLockableFlag { // no need to test trackNotLockableFlag, if we got here we're disabling lockableArg = " " + git.LockableAttrib diff -Nru git-lfs-2.10.0/commands/run.go git-lfs-2.11.0/commands/run.go --- git-lfs-2.10.0/commands/run.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/run.go 2020-05-08 16:06:35.000000000 +0000 @@ -69,6 +69,12 @@ Run: func(c *cobra.Command, args []string) { cmd, _, e := c.Root().Find(args) + // In the case of "git lfs help config", pretend the + // last arg was "help" so our command lookup succeeds, + // since cmd will be ignored in helpCommand(). + if e != nil && args[0] == "config" { + cmd, _, e = c.Root().Find([]string{"help"}) + } if cmd == nil || e != nil { c.Printf("Unknown help topic %#q\n", args) c.Root().Usage() @@ -124,6 +130,9 @@ } func printHelp(commandName string) { + if commandName == "--help" { + commandName = "git-lfs" + } if txt, ok := ManPages[commandName]; ok { fmt.Fprintf(os.Stdout, "%s\n", strings.TrimSpace(txt)) } else { diff -Nru git-lfs-2.10.0/commands/uploader.go git-lfs-2.11.0/commands/uploader.go --- git-lfs-2.10.0/commands/uploader.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/commands/uploader.go 2020-05-08 16:06:35.000000000 +0000 @@ -258,7 +258,7 @@ ExitWithError(err) } - q.Add(t.Name, t.Path, t.Oid, t.Size, t.Missing) + q.Add(t.Name, t.Path, t.Oid, t.Size, t.Missing, nil) c.SetUploaded(p.Oid) } } diff -Nru git-lfs-2.10.0/config/config.go git-lfs-2.11.0/config/config.go --- git-lfs-2.10.0/config/config.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/config/config.go 2020-05-08 16:06:35.000000000 +0000 @@ -268,7 +268,10 @@ func (c *Configuration) SetValidRemote(name string) error { if err := git.ValidateRemote(name); err != nil { - return err + name := git.RewriteLocalPathAsURL(name) + if err := git.ValidateRemote(name); err != nil { + return err + } } c.SetRemote(name) return nil @@ -276,7 +279,10 @@ func (c *Configuration) SetValidPushRemote(name string) error { if err := git.ValidateRemote(name); err != nil { - return err + name := git.RewriteLocalPathAsURL(name) + if err := git.ValidateRemote(name); err != nil { + return err + } } c.SetPushRemote(name) return nil diff -Nru git-lfs-2.10.0/config/version.go git-lfs-2.11.0/config/version.go --- git-lfs-2.10.0/config/version.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/config/version.go 2020-05-08 16:06:35.000000000 +0000 @@ -13,7 +13,7 @@ ) const ( - Version = "2.10.0" + Version = "2.11.0" ) func init() { diff -Nru git-lfs-2.10.0/debian/changelog git-lfs-2.11.0/debian/changelog --- git-lfs-2.10.0/debian/changelog 2020-01-21 23:00:15.000000000 +0000 +++ git-lfs-2.11.0/debian/changelog 2020-05-10 04:13:47.000000000 +0000 @@ -1,3 +1,9 @@ +git-lfs (2.11.0-1) unstable; urgency=medium + + * New upstream release + + -- Stephen Gelman Sat, 09 May 2020 23:13:47 -0500 + git-lfs (2.10.0-1) unstable; urgency=medium * New upstream release diff -Nru git-lfs-2.10.0/docker/run_dockers.bsh git-lfs-2.11.0/docker/run_dockers.bsh --- git-lfs-2.10.0/docker/run_dockers.bsh 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/docker/run_dockers.bsh 2020-05-08 16:06:35.000000000 +0000 @@ -42,8 +42,11 @@ # Parse Arguments IMAGES=() +PRUNE= while [[ $# > 0 ]]; do - if [ "$1" == "--" ]; then + if [ "$1" = "--prune" ]; then + PRUNE=t + elif [ "$1" == "--" ]; then shift DOCKER_CMD="${@}" break @@ -108,6 +111,10 @@ -v "${MINGW_PATCH}${IMAGE_REPO_DIR}:/repo" \ gitlfs/build-dockers:${IMAGE_NAME} ${DOCKER_CMD-} + if [ -n "$PRUNE" ] + then + $SUDO docker rmi -f "gitlfs/build-dockers:${IMAGE_NAME}" + fi done echo "Docker run completed successfully!" diff -Nru git-lfs-2.10.0/docs/man/git-lfs.1.ronn git-lfs-2.11.0/docs/man/git-lfs.1.ronn --- git-lfs-2.10.0/docs/man/git-lfs.1.ronn 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/docs/man/git-lfs.1.ronn 2020-05-08 16:06:35.000000000 +0000 @@ -33,6 +33,10 @@ Display the Git LFS environment. * git-lfs-checkout(1): Populate working copy with real content from Git LFS files. +* git-lfs-dedup(1): + De-duplicate Git LFS files. +* git-lfs-ext(1): + Display Git LFS extension details. * git-lfs-fetch(1): Download Git LFS files from a remote. * git-lfs-fsck(1): @@ -75,14 +79,22 @@ * git-lfs-clean(1): Git clean filter that converts large files to pointers. +* git-lfs-filter-process(1): + Git process filter that converts between large files and pointers. * git-lfs-pointer(1): Build and compare pointers. +* git-lfs-post-checkout(1): + Git post-checkout hook implementation. +* git-lfs-post-commit(1): + Git post-commit hook implementation. +* git-lfs-post-merge(1): + Git post-merge hook implementation. * git-lfs-pre-push(1): Git pre-push hook implementation. -* git-lfs-filter-process(1): - Git process filter that converts between large files and pointers. * git-lfs-smudge(1): Git smudge filter that converts pointer in blobs to the actual content. +* git-lfs-standalone-file(1): + Git LFS standalone transfer adapter for file URLs (local paths). ## EXAMPLES diff -Nru git-lfs-2.10.0/docs/man/git-lfs-config.5.ronn git-lfs-2.11.0/docs/man/git-lfs-config.5.ronn --- git-lfs-2.10.0/docs/man/git-lfs-config.5.ronn 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/docs/man/git-lfs-config.5.ronn 2020-05-08 16:06:35.000000000 +0000 @@ -83,6 +83,12 @@ Default: `lfs` in Git repository directory (usually `.git/lfs`). +* `lfs.largefilewarning` + + Warn when a file is 4 GiB or larger. Such files will be corrupted when using + Windows (unless smudging is disabled) due to a limitation in Git. Default: + true. + ### Transfer (upload / download) settings These settings control how the upload and download of LFS content occurs. @@ -154,6 +160,18 @@ not an integer, is less than one, or is not given, a value of eight will be used instead. +* `lfs.transfer.maxretrydelay` + + Specifies the maximum time in seconds LFS will wait between each retry + attempt. LFS uses exponential backoff for retries, doubling the time between + each retry until reaching this limit. If a server requests a delay using the + `Retry-After` header, the header value overrides the exponential delay for + that attempt and is not limited by this option. + + Must be an integer which is not negative. Use zero to disable delays between + retries unless requested by a server. If the value is not an integer, is + negative, or is not given, a value of ten will be used instead. + * `lfs.transfer.maxverifies` Specifies how many verification requests LFS will attempt per OID before @@ -373,9 +391,6 @@ - lfs.locksverify - lfs.pushurl - lfs.url -- lfs.extension.{name}.clean -- lfs.extension.{name}.smudge -- lfs.extension.{name}.priority - remote.{name}.lfsurl - lfs.{*}.access diff -Nru git-lfs-2.10.0/docs/man/git-lfs-dedup.1.ronn git-lfs-2.11.0/docs/man/git-lfs-dedup.1.ronn --- git-lfs-2.10.0/docs/man/git-lfs-dedup.1.ronn 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/docs/man/git-lfs-dedup.1.ronn 2020-05-08 16:06:35.000000000 +0000 @@ -12,6 +12,11 @@ If the operating system or file system don't support copy-on-write file creation, this command exits unsuccessfully. +This command will also exit without success if any Git LFS extensions are +configured, as these will typically be used to alter the file contents +before they are written to the Git LFS storage directory, and therefore the +working tree files should not be copy-on-write clones of the LFS object files. + ## SEE ALSO Part of the git-lfs(1) suite. diff -Nru git-lfs-2.10.0/docs/man/git-lfs-migrate.1.ronn git-lfs-2.11.0/docs/man/git-lfs-migrate.1.ronn --- git-lfs-2.10.0/docs/man/git-lfs-migrate.1.ronn 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/docs/man/git-lfs-migrate.1.ronn 2020-05-08 16:06:35.000000000 +0000 @@ -35,6 +35,12 @@ * `--everything`: See [INCLUDE AND EXCLUDE (REFS)]. +* `--yes`: + Assume a yes answer to any prompts, permitting noninteractive use. + Currently, the only such prompt is the one asking whether to overwrite + (destroy) any working copy changes. Thus, specifying this option may cause + data loss if you are not careful. + * [branch ...]: Migrate only the set of branches listed. If not given, `git-lfs-migrate(1)` will migrate the currently checked out branch. diff -Nru git-lfs-2.10.0/docs/man/git-lfs-post-commit.1.ronn git-lfs-2.11.0/docs/man/git-lfs-post-commit.1.ronn --- git-lfs-2.10.0/docs/man/git-lfs-post-commit.1.ronn 1970-01-01 00:00:00.000000000 +0000 +++ git-lfs-2.11.0/docs/man/git-lfs-post-commit.1.ronn 2020-05-08 16:06:35.000000000 +0000 @@ -0,0 +1,23 @@ +git-lfs-post-commit(1) -- Git post-commit hook implementation +================================================================= + +## SYNOPSIS + +`git lfs post-commit` + +## DESCRIPTION + +Responds to Git post-commit events. It makes sure that any files which are +marked as lockable by `git lfs track` are read-only in the working copy, if +not currently locked by the local user. + +Where the `git lfs post-merge` command, which has a similar purpose, must +examine all files in the working copy, `git lfs post-commit` can limit +itself checking only those files which have changed in `HEAD`. It primarily +handles newly added lockable files which have not yet been made read-only. + +## SEE ALSO + +git-lfs-post-merge(1), git-lfs-track(1) + +Part of the git-lfs(1) suite. diff -Nru git-lfs-2.10.0/git/git.go git-lfs-2.11.0/git/git.go --- git-lfs-2.10.0/git/git.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/git/git.go 2020-05-08 16:06:35.000000000 +0000 @@ -31,6 +31,7 @@ RefTypeLocalBranch = RefType(iota) RefTypeRemoteBranch = RefType(iota) RefTypeLocalTag = RefType(iota) + RefTypeRemoteTag = RefType(iota) RefTypeHEAD = RefType(iota) // current checkout RefTypeOther = RefType(iota) // stash or unknown @@ -352,6 +353,54 @@ return ret, nil } +func RemoteURLs(push bool) (map[string][]string, error) { + cmd := gitNoLFS("remote", "-v") + + outp, err := cmd.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("failed to call git remote -v: %v", err) + } + cmd.Start() + defer cmd.Wait() + + scanner := bufio.NewScanner(outp) + + text := "(fetch)" + if push { + text = "(push)" + } + ret := make(map[string][]string) + for scanner.Scan() { + // [remote, urlpair-text] + pair := strings.Split(strings.TrimSpace(scanner.Text()), "\t") + if len(pair) != 2 { + continue + } + // [url, "(fetch)" | "(push)"] + urlpair := strings.Split(pair[1], " ") + if len(urlpair) != 2 || urlpair[1] != text { + continue + } + ret[pair[0]] = append(ret[pair[0]], urlpair[0]) + } + + return ret, nil +} + +func MapRemoteURL(url string, push bool) (string, bool) { + urls, err := RemoteURLs(push) + if err != nil { + return url, false + } + + for name, remotes := range urls { + if len(remotes) == 1 && url == remotes[0] { + return name, true + } + } + return url, false +} + // Refs returns all of the local and remote branches and tags for the current // repository. Other refs (HEAD, refs/stash, git notes) are ignored. func LocalRefs() ([]*Ref, error) { @@ -454,6 +503,34 @@ } } +func RewriteLocalPathAsURL(path string) string { + var slash string + if abs, err := filepath.Abs(path); err == nil { + // Required for Windows paths to work. + if !strings.HasPrefix(abs, "/") { + slash = "/" + } + path = abs + } + + var gitpath string + if filepath.Base(path) == ".git" { + gitpath = path + path = filepath.Dir(path) + } else { + gitpath = filepath.Join(path, ".git") + } + + if _, err := os.Stat(gitpath); err == nil { + path = gitpath + } else if _, err := os.Stat(path); err != nil { + // Not a local path. We check down here because we perform + // canonicalization by stripping off the .git above. + return path + } + return fmt.Sprintf("file://%s%s", slash, filepath.ToSlash(path)) +} + func UpdateIndexFromStdin() *subprocess.Cmd { return git("update-index", "-q", "--refresh", "--stdin") } @@ -1042,7 +1119,7 @@ } // RemoteRefs returns a list of branches & tags for a remote by actually -// accessing the remote vir git ls-remote +// accessing the remote via git ls-remote. func RemoteRefs(remoteName string) ([]*Ref, error) { var ret []*Ref cmd := gitNoLFS("ls-remote", "--heads", "--tags", "-q", remoteName) @@ -1064,7 +1141,11 @@ } sha := match[1] - ret = append(ret, &Ref{name, RefTypeRemoteBranch, sha}) + typ := RefTypeRemoteBranch + if match[2] == "tags" { + typ = RefTypeRemoteTag + } + ret = append(ret, &Ref{name, typ, sha}) } } return ret, cmd.Wait() diff -Nru git-lfs-2.10.0/git/git_test.go git-lfs-2.11.0/git/git_test.go --- git-lfs-2.10.0/git/git_test.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/git/git_test.go 2020-05-08 16:06:35.000000000 +0000 @@ -702,3 +702,90 @@ assert.Equal(t, expected.Ok, ok) } } + +func TestRemoteURLs(t *testing.T) { + repo := test.NewRepo(t) + repo.Pushd() + defer func() { + repo.Popd() + repo.Cleanup() + }() + cfg := repo.GitConfig() + cfg.SetLocal("remote.foo.url", "https://github.com/git-lfs/git-lfs.git") + cfg.SetLocal("remote.bar.url", "https://github.com/git-lfs/wildmatch.git") + cfg.SetLocal("remote.bar.pushurl", "https://github.com/git-lfs/pktline.git") + + expected := make(map[string][]string) + expected["foo"] = []string{"https://github.com/git-lfs/git-lfs.git"} + expected["bar"] = []string{"https://github.com/git-lfs/wildmatch.git"} + actual, err := RemoteURLs(false) + assert.Nil(t, err) + assert.Equal(t, expected, actual) + + expected["bar"] = []string{"https://github.com/git-lfs/pktline.git"} + actual, err = RemoteURLs(true) + assert.Nil(t, err) + assert.Equal(t, expected, actual) +} + +func TestMapRemoteURL(t *testing.T) { + repo := test.NewRepo(t) + repo.Pushd() + defer func() { + repo.Popd() + repo.Cleanup() + }() + cfg := repo.GitConfig() + cfg.SetLocal("remote.foo.url", "https://github.com/git-lfs/git-lfs.git") + cfg.SetLocal("remote.bar.url", "https://github.com/git-lfs/wildmatch.git") + cfg.SetLocal("remote.bar.pushurl", "https://github.com/git-lfs/pktline.git") + + tests := []struct { + url string + push bool + match bool + val string + }{ + { + "https://github.com/git-lfs/git-lfs.git", + false, + true, + "foo", + }, + { + "https://github.com/git-lfs/git-lfs.git", + true, + true, + "foo", + }, + { + "https://github.com/git-lfs/wildmatch.git", + false, + true, + "bar", + }, + { + "https://github.com/git-lfs/pktline.git", + true, + true, + "bar", + }, + { + "https://github.com/git-lfs/pktline.git", + false, + false, + "https://github.com/git-lfs/pktline.git", + }, + { + "https://github.com/git/git.git", + true, + false, + "https://github.com/git/git.git", + }, + } + for _, test := range tests { + val, ok := MapRemoteURL(test.url, test.push) + assert.Equal(t, ok, test.match) + assert.Equal(t, val, test.val) + } +} diff -Nru git-lfs-2.10.0/git/rev_list_scanner.go git-lfs-2.11.0/git/rev_list_scanner.go --- git-lfs-2.10.0/git/rev_list_scanner.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/git/rev_list_scanner.go 2020-05-08 16:06:35.000000000 +0000 @@ -225,7 +225,7 @@ // occurred. func revListArgs(include, exclude []string, opt *ScanRefsOptions) (io.Reader, []string, error) { var stdin io.Reader - args := []string{"rev-list", "--stdin"} + args := []string{"rev-list"} if !opt.CommitsOnly { args = append(args, "--objects") } @@ -251,6 +251,7 @@ case ScanAllMode: args = append(args, "--all") case ScanRangeToRemoteMode: + args = append(args, "--ignore-missing") if len(opt.SkippedRefs) == 0 { args = append(args, "--not", "--remotes="+opt.Remote) stdin = strings.NewReader(strings.Join( @@ -263,7 +264,7 @@ default: return nil, nil, errors.Errorf("unknown scan type: %d", opt.Mode) } - return stdin, append(args, "--"), nil + return stdin, append(args, "--stdin", "--"), nil } func includeExcludeShas(include, exclude []string) []string { diff -Nru git-lfs-2.10.0/git/rev_list_scanner_test.go git-lfs-2.11.0/git/rev_list_scanner_test.go --- git-lfs-2.10.0/git/rev_list_scanner_test.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/git/rev_list_scanner_test.go 2020-05-08 16:06:35.000000000 +0000 @@ -57,7 +57,7 @@ SkipDeletedBlobs: false, }, ExpectedStdin: fmt.Sprintf("%s\n^%s", s1, s2), - ExpectedArgs: []string{"rev-list", "--stdin", "--objects", "--do-walk", "--"}, + ExpectedArgs: []string{"rev-list", "--objects", "--do-walk", "--stdin", "--"}, }, "scan refs not deleted, left and right": { Include: []string{s1}, Exclude: []string{s2}, Opt: &ScanRefsOptions{ @@ -65,7 +65,7 @@ SkipDeletedBlobs: true, }, ExpectedStdin: fmt.Sprintf("%s\n^%s", s1, s2), - ExpectedArgs: []string{"rev-list", "--stdin", "--objects", "--no-walk", "--"}, + ExpectedArgs: []string{"rev-list", "--objects", "--no-walk", "--stdin", "--"}, }, "scan refs deleted, left only": { Include: []string{s1}, Opt: &ScanRefsOptions{ @@ -73,7 +73,7 @@ SkipDeletedBlobs: false, }, ExpectedStdin: s1, - ExpectedArgs: []string{"rev-list", "--stdin", "--objects", "--do-walk", "--"}, + ExpectedArgs: []string{"rev-list", "--objects", "--do-walk", "--stdin", "--"}, }, "scan refs not deleted, left only": { Include: []string{s1}, Opt: &ScanRefsOptions{ @@ -81,13 +81,13 @@ SkipDeletedBlobs: true, }, ExpectedStdin: s1, - ExpectedArgs: []string{"rev-list", "--stdin", "--objects", "--no-walk", "--"}, + ExpectedArgs: []string{"rev-list", "--objects", "--no-walk", "--stdin", "--"}, }, "scan all": { Include: []string{s1}, Exclude: []string{s2}, Opt: &ScanRefsOptions{ Mode: ScanAllMode, }, - ExpectedArgs: []string{"rev-list", "--stdin", "--objects", "--all", "--"}, + ExpectedArgs: []string{"rev-list", "--objects", "--all", "--stdin", "--"}, }, "scan left to remote, no skipped refs": { Include: []string{s1}, Opt: &ScanRefsOptions{ @@ -96,7 +96,7 @@ SkippedRefs: []string{}, }, ExpectedStdin: s1, - ExpectedArgs: []string{"rev-list", "--stdin", "--objects", "--not", "--remotes=origin", "--"}, + ExpectedArgs: []string{"rev-list", "--objects", "--ignore-missing", "--not", "--remotes=origin", "--stdin", "--"}, }, "scan left to remote, skipped refs": { Include: []string{s1}, Exclude: []string{s2}, Opt: &ScanRefsOptions{ @@ -104,7 +104,7 @@ Remote: "origin", SkippedRefs: []string{"a", "b", "c"}, }, - ExpectedArgs: []string{"rev-list", "--stdin", "--objects", "--"}, + ExpectedArgs: []string{"rev-list", "--objects", "--ignore-missing", "--stdin", "--"}, ExpectedStdin: s1 + "\n^" + s2 + "\na\nb\nc", }, "scan unknown type": { @@ -119,7 +119,7 @@ Order: DateRevListOrder, }, ExpectedStdin: fmt.Sprintf("%s\n^%s", s1, s2), - ExpectedArgs: []string{"rev-list", "--stdin", "--objects", "--date-order", "--do-walk", "--"}, + ExpectedArgs: []string{"rev-list", "--objects", "--date-order", "--do-walk", "--stdin", "--"}, }, "scan author date order": { Include: []string{s1}, Exclude: []string{s2}, Opt: &ScanRefsOptions{ @@ -127,7 +127,7 @@ Order: AuthorDateRevListOrder, }, ExpectedStdin: fmt.Sprintf("%s\n^%s", s1, s2), - ExpectedArgs: []string{"rev-list", "--stdin", "--objects", "--author-date-order", "--do-walk", "--"}, + ExpectedArgs: []string{"rev-list", "--objects", "--author-date-order", "--do-walk", "--stdin", "--"}, }, "scan topo order": { Include: []string{s1}, Exclude: []string{s2}, Opt: &ScanRefsOptions{ @@ -135,7 +135,7 @@ Order: TopoRevListOrder, }, ExpectedStdin: fmt.Sprintf("%s\n^%s", s1, s2), - ExpectedArgs: []string{"rev-list", "--stdin", "--objects", "--topo-order", "--do-walk", "--"}, + ExpectedArgs: []string{"rev-list", "--objects", "--topo-order", "--do-walk", "--stdin", "--"}, }, "scan commits only": { Include: []string{s1}, Exclude: []string{s2}, Opt: &ScanRefsOptions{ @@ -143,7 +143,7 @@ CommitsOnly: true, }, ExpectedStdin: fmt.Sprintf("%s\n^%s", s1, s2), - ExpectedArgs: []string{"rev-list", "--stdin", "--do-walk", "--"}, + ExpectedArgs: []string{"rev-list", "--do-walk", "--stdin", "--"}, }, "scan reverse": { Include: []string{s1}, Exclude: []string{s2}, Opt: &ScanRefsOptions{ @@ -151,7 +151,7 @@ Reverse: true, }, ExpectedStdin: fmt.Sprintf("%s\n^%s", s1, s2), - ExpectedArgs: []string{"rev-list", "--stdin", "--objects", "--reverse", "--do-walk", "--"}, + ExpectedArgs: []string{"rev-list", "--objects", "--reverse", "--do-walk", "--stdin", "--"}, }, } { t.Run(desc, c.Assert) diff -Nru git-lfs-2.10.0/.github/workflows/ci.yml git-lfs-2.11.0/.github/workflows/ci.yml --- git-lfs-2.10.0/.github/workflows/ci.yml 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/.github/workflows/ci.yml 2020-05-08 16:06:35.000000000 +0000 @@ -11,7 +11,7 @@ steps: - uses: actions/checkout@v1 - run: script/cibuild - - run: make release + - run: CGO_ENABLED=0 make release - run: mkdir -p bin/assets - run: find bin/releases -name "*$(uname -s | tr A-Z a-z)*" | xargs -I{} cp {} bin/assets - uses: actions/upload-artifact@v1 @@ -49,9 +49,13 @@ shell: bash - run: GOPATH="$HOME/go" PATH="$HOME/go/bin:$PATH" script/cibuild shell: bash + - run: rm -f commands/mancontent_gen.go + shell: bash - run: GOPATH="$HOME/go" PATH="$HOME/go/bin:$PATH" make GOARCH=386 -B shell: bash - run: mv bin\git-lfs.exe git-lfs-x86.exe + - run: rm -f commands/mancontent_gen.go + shell: bash - run: GOPATH="$HOME/go" PATH="$HOME/go/bin:$PATH" make GOARCH=amd64 -B shell: bash - run: mv bin\git-lfs.exe git-lfs-x64.exe @@ -94,4 +98,4 @@ - uses: actions/setup-ruby@v1 - run: git clone https://github.com/git-lfs/build-dockers.git "$HOME/build-dockers" - run: (cd "$HOME/build-dockers" && ./build_dockers.bsh) - - run: DOCKER_AUTOPULL=0 ./docker/run_dockers.bsh + - run: DOCKER_AUTOPULL=0 ./docker/run_dockers.bsh --prune diff -Nru git-lfs-2.10.0/.github/workflows/release.yml git-lfs-2.11.0/.github/workflows/release.yml --- git-lfs-2.10.0/.github/workflows/release.yml 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/.github/workflows/release.yml 2020-05-08 16:06:35.000000000 +0000 @@ -55,7 +55,7 @@ - uses: actions/download-artifact@v1 with: name: assets - - run: make release + - run: CGO_ENABLED=0 make release - run: rm -f bin/releases/*windows* - run: find assets -name "*windows*" | xargs -L1 -I{} mv {} bin/releases - run: script/upload --skip-verify $(git describe) @@ -70,7 +70,7 @@ - run: gem install packagecloud-ruby - run: git clone https://github.com/git-lfs/build-dockers.git "$HOME/build-dockers" - run: (cd "$HOME/build-dockers" && ./build_dockers.bsh) - - run: DOCKER_AUTOPULL=0 ./docker/run_dockers.bsh + - run: DOCKER_AUTOPULL=0 ./docker/run_dockers.bsh --prune # If this is a pre-release tag, don't upload anything to packagecloud. - run: '[ -z "${GITHUB_REF%%refs/tags/*-pre*}" ] || ./script/packagecloud.rb' env: diff -Nru git-lfs-2.10.0/go.mod git-lfs-2.11.0/go.mod --- git-lfs-2.10.0/go.mod 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/go.mod 2020-05-08 16:06:35.000000000 +0000 @@ -10,8 +10,6 @@ github.com/git-lfs/wildmatch v1.0.4 github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jcmturner/gofork v1.0.0 // indirect - github.com/jcmturner/gokrb5 v7.3.0+incompatible // indirect - github.com/kr/pty v1.1.8 // indirect github.com/mattn/go-isatty v0.0.4 github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17 @@ -19,16 +17,16 @@ github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 // indirect github.com/ssgelm/cookiejarparser v1.0.1 - github.com/stretchr/testify v1.2.2 + github.com/stretchr/objx v0.2.0 // indirect + github.com/stretchr/testify v1.5.1 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v0.0.0-20170210233622-6b67b3fab74d golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 golang.org/x/sys v0.0.0-20190412213103-97732733099d - gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect - gopkg.in/jcmturner/gokrb5.v7 v7.3.0 // indirect - gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect + gopkg.in/jcmturner/goidentity.v2 v2.0.0 // indirect + gopkg.in/yaml.v2 v2.2.8 // indirect ) go 1.11 diff -Nru git-lfs-2.10.0/go.sum git-lfs-2.11.0/go.sum --- git-lfs-2.10.0/go.sum 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/go.sum 2020-05-08 16:06:35.000000000 +0000 @@ -2,8 +2,7 @@ github.com/alexbrainman/sspi v0.0.0-20180125232955-4729b3d4d858/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro= github.com/avast/retry-go v2.4.2+incompatible h1:+ZjCypQT/CyP0kyJO2EcU4d/ZEJWSbP8NENI578cPmA= github.com/avast/retry-go v2.4.2+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= -github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dpotapov/go-spnego v0.0.0-20190506202455-c2c609116ad0 h1:Hhh7nu7CfFVlnBJqmDDUh+j1H5fqjLMzM4czZzNNJGM= @@ -12,25 +11,18 @@ github.com/git-lfs/gitobj v1.4.1/go.mod h1:B+djgKTnUoJHbg4uDvnC/+6xPcfEJNFbZd/YunEJRtA= github.com/git-lfs/go-netrc v0.0.0-20180525200031-e0e9ca483a18 h1:7Th0eBA4rT8WJNiM1vppjaIv9W5WJinhpbCJvRJxloI= github.com/git-lfs/go-netrc v0.0.0-20180525200031-e0e9ca483a18/go.mod h1:70O4NAtvWn1jW8V8V+OKrJJYcxDLTmIozfi2fmSz5SI= -github.com/git-lfs/go-ntlm v0.0.0-20190307203151-c5056e7fa066 h1:j7JwIEwLNQ/kBdKpHO3U1jjMXIPjSq7eCFvQIF3e8Fs= -github.com/git-lfs/go-ntlm v0.0.0-20190307203151-c5056e7fa066/go.mod h1:YnCP1lAyul0ITv9nT/OqXseZmGeaqvMVa2uvl8ssQvE= github.com/git-lfs/go-ntlm v0.0.0-20190401175752-c5056e7fa066 h1:f5UyyCnv3o2EHy+zsqOyYa8jB5bZR/N9ZEideqeDYag= github.com/git-lfs/go-ntlm v0.0.0-20190401175752-c5056e7fa066/go.mod h1:YnCP1lAyul0ITv9nT/OqXseZmGeaqvMVa2uvl8ssQvE= -github.com/git-lfs/wildmatch v1.0.2 h1:Bt8fjbL2OjfhSTsmLPXSalpXgX9twSIFSVvupY2IdbE= -github.com/git-lfs/wildmatch v1.0.2/go.mod h1:SdHAGnApDpnFYQ0vAxbniWR0sn7yLJ3QXo9RRfhn2ew= github.com/git-lfs/wildmatch v1.0.4 h1:Mj6LPnNZ6QSHLAAPDCH596pu6A/Z1xVm2Vk0+s3CtkY= github.com/git-lfs/wildmatch v1.0.4/go.mod h1:SdHAGnApDpnFYQ0vAxbniWR0sn7yLJ3QXo9RRfhn2ew= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03 h1:FUwcHNlEqkqLjLBdCp5PRlCFijNjvcYANOZXzCfXwCM= github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8= github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jcmturner/gokrb5 v7.3.0+incompatible h1:49JOw6yjCQqdwxylFjI54Sue6lZftMcm4+LSss/48ss= -github.com/jcmturner/gokrb5 v7.3.0+incompatible/go.mod h1:0Q5eFyVvYsEsZ8xl1A/jUqhXvxUp/X9ELrJm+zieq5E= -github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw= @@ -47,8 +39,15 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/ssgelm/cookiejarparser v1.0.1 h1:cRdXauUbOTFzTPJFaeiWbHnQ+tRGlpKKzvIK9PUekE4= github.com/ssgelm/cookiejarparser v1.0.1/go.mod h1:DUfC0mpjIzlDN7DzKjXpHj0qMI5m9VrZuz3wSlI+OEI= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -71,17 +70,18 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw= gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM= gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= -gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI= -gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= +gopkg.in/jcmturner/goidentity.v2 v2.0.0 h1:6Bmcdaxb0dD3HyHbo/MtJ2Q1wXLDuZJFwXZmuZvM+zw= +gopkg.in/jcmturner/goidentity.v2 v2.0.0/go.mod h1:vCwK9HeXksMeUmQ4SxDd1tRz4LejrKh3KRVjQWhjvZI= gopkg.in/jcmturner/gokrb5.v5 v5.3.0 h1:RS1MYApX27Hx1Xw7NECs7XxGxxrm69/4OmaRuX9kwec= gopkg.in/jcmturner/gokrb5.v5 v5.3.0/go.mod h1:oQz8Wc5GsctOTgCVyKad1Vw4TCWz5G6gfIQr88RPv4k= -gopkg.in/jcmturner/gokrb5.v7 v7.3.0 h1:0709Jtq/6QXEuWRfAm260XqlpcwL1vxtO1tUE2qK8Z4= -gopkg.in/jcmturner/gokrb5.v7 v7.3.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= gopkg.in/jcmturner/rpc.v0 v0.0.2 h1:wBTgrbL1qmLBUPsYVCqdJiI5aJgQhexmK+JkTHPUNJI= gopkg.in/jcmturner/rpc.v0 v0.0.2/go.mod h1:NzMq6cRzR9lipgw7WxRBHNx5N8SifBuaCQsOT1kWY/E= -gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU= -gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff -Nru git-lfs-2.10.0/lfs/gitfilter_smudge.go git-lfs-2.11.0/lfs/gitfilter_smudge.go --- git-lfs-2.10.0/lfs/gitfilter_smudge.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/lfs/gitfilter_smudge.go 2020-05-08 16:06:35.000000000 +0000 @@ -101,7 +101,7 @@ tq.WithProgressCallback(cb), tq.RemoteRef(f.RemoteRef()), ) - q.Add(filepath.Base(workingfile), mediafile, ptr.Oid, ptr.Size, false) + q.Add(filepath.Base(workingfile), mediafile, ptr.Oid, ptr.Size, false, nil) q.Wait() if errs := q.Errors(); len(errs) > 0 { diff -Nru git-lfs-2.10.0/lfsapi/endpoint_finder_test.go git-lfs-2.11.0/lfsapi/endpoint_finder_test.go --- git-lfs-2.10.0/lfsapi/endpoint_finder_test.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/lfsapi/endpoint_finder_test.go 2020-05-08 16:06:35.000000000 +0000 @@ -1,6 +1,8 @@ package lfsapi import ( + "io/ioutil" + "os" "runtime" "testing" @@ -313,32 +315,92 @@ assert.Equal(t, "", e.SshPort) } -func TestLocalPathEndpointAddsDotGitDir(t *testing.T) { +func TestLocalPathEndpointAddsDotGitForWorkingRepo(t *testing.T) { // Windows will add a drive letter to the paths below since we // canonicalize them. if runtime.GOOS == "windows" { return } + path, err := ioutil.TempDir("", "lfsRepo") + assert.Nil(t, err) + path = path + "/local/path" + err = os.MkdirAll(path+"/.git", 0755) + assert.Nil(t, err) + + finder := NewEndpointFinder(lfshttp.NewContext(nil, nil, map[string]string{ + "remote.origin.url": path, + })) + e := finder.Endpoint("download", "") + assert.Equal(t, "file://"+path+"/.git", e.Url) + + os.RemoveAll(path) +} + +func TestLocalPathEndpointPreservesDotGitForWorkingRepo(t *testing.T) { + // Windows will add a drive letter to the paths below since we + // canonicalize them. + if runtime.GOOS == "windows" { + return + } + + path, err := ioutil.TempDir("", "lfsRepo") + assert.Nil(t, err) + path = path + "/local/path/.git" + err = os.MkdirAll(path, 0755) + assert.Nil(t, err) + + finder := NewEndpointFinder(lfshttp.NewContext(nil, nil, map[string]string{ + "remote.origin.url": path, + })) + e := finder.Endpoint("download", "") + assert.Equal(t, "file://"+path, e.Url) + + os.RemoveAll(path) +} + +func TestLocalPathEndpointPreservesNoDotGitForBareRepo(t *testing.T) { + // Windows will add a drive letter to the paths below since we + // canonicalize them. + if runtime.GOOS == "windows" { + return + } + + path, err := ioutil.TempDir("", "lfsRepo") + assert.Nil(t, err) + path = path + "/local/path" + err = os.MkdirAll(path, 0755) + assert.Nil(t, err) + finder := NewEndpointFinder(lfshttp.NewContext(nil, nil, map[string]string{ - "remote.origin.url": "/local/path", + "remote.origin.url": path, })) e := finder.Endpoint("download", "") - assert.Equal(t, "file:///local/path/.git", e.Url) + assert.Equal(t, "file://"+path, e.Url) + + os.RemoveAll(path) } -func TestLocalPathEndpointPreservesDotGit(t *testing.T) { +func TestLocalPathEndpointRemovesDotGitForBareRepo(t *testing.T) { // Windows will add a drive letter to the paths below since we // canonicalize them. if runtime.GOOS == "windows" { return } + path, err := ioutil.TempDir("", "lfsRepo") + assert.Nil(t, err) + path = path + "/local/path" + err = os.MkdirAll(path, 0755) + assert.Nil(t, err) + finder := NewEndpointFinder(lfshttp.NewContext(nil, nil, map[string]string{ - "remote.origin.url": "/local/path.git", + "remote.origin.url": path + "/.git", })) e := finder.Endpoint("download", "") - assert.Equal(t, "file:///local/path.git", e.Url) + assert.Equal(t, "file://"+path, e.Url) + + os.RemoveAll(path) } func TestAccessConfig(t *testing.T) { diff -Nru git-lfs-2.10.0/lfshttp/client.go git-lfs-2.11.0/lfshttp/client.go --- git-lfs-2.10.0/lfshttp/client.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/lfshttp/client.go 2020-05-08 16:06:35.000000000 +0000 @@ -449,7 +449,9 @@ tr.DialContext = dialer.DialContext } - tr.TLSClientConfig = &tls.Config{} + tr.TLSClientConfig = &tls.Config{ + Renegotiation: tls.RenegotiateFreelyAsClient, + } if isClientCertEnabledForHost(c, host) { tracerx.Printf("http: client cert for %s", host) diff -Nru git-lfs-2.10.0/lfshttp/endpoint.go git-lfs-2.11.0/lfshttp/endpoint.go --- git-lfs-2.10.0/lfshttp/endpoint.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/lfshttp/endpoint.go 2020-05-08 16:06:35.000000000 +0000 @@ -3,9 +3,10 @@ import ( "fmt" "net/url" - "path/filepath" "regexp" "strings" + + "github.com/git-lfs/git-lfs/git" ) const UrlUnknown = "" @@ -108,18 +109,7 @@ } func EndpointFromLocalPath(path string) Endpoint { - if !strings.HasSuffix(path, ".git") { - path = fmt.Sprintf("%s/.git", path) - } - var slash string - if abs, err := filepath.Abs(path); err == nil { - // Required for Windows paths to work. - if !strings.HasPrefix(abs, "/") { - slash = "/" - } - path = abs - } - return Endpoint{Url: fmt.Sprintf("file://%s%s", slash, filepath.ToSlash(path))} + return Endpoint{Url: git.RewriteLocalPathAsURL(path)} } // Construct a new endpoint from a file URL diff -Nru git-lfs-2.10.0/lfshttp/standalone/standalone.go git-lfs-2.11.0/lfshttp/standalone/standalone.go --- git-lfs-2.10.0/lfshttp/standalone/standalone.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/lfshttp/standalone/standalone.go 2020-05-08 16:06:35.000000000 +0000 @@ -101,6 +101,11 @@ } env = env[:n] + // Trim any trailing .git path segment. + if filepath.Base(path) == ".git" { + path = filepath.Dir(path) + } + curdir, err := os.Getwd() if err != nil { return "", err diff -Nru git-lfs-2.10.0/LICENSE.md git-lfs-2.11.0/LICENSE.md --- git-lfs-2.10.0/LICENSE.md 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/LICENSE.md 2020-05-08 16:06:35.000000000 +0000 @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2014-2018 GitHub, Inc. and Git LFS contributors +Copyright (c) 2014-2020 GitHub, Inc. and Git LFS contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -Nru git-lfs-2.10.0/Makefile git-lfs-2.11.0/Makefile --- git-lfs-2.10.0/Makefile 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/Makefile 2020-05-08 16:06:35.000000000 +0000 @@ -404,9 +404,9 @@ # make PKGS="config lfsapi tools/kv" test-race # # And so on. -test : fmt +test : fmt $(.DEFAULT_GOAL) (unset GIT_DIR; unset GIT_WORK_TREE; \ - $(GO) test $(GO_TEST_EXTRA_ARGS) $(addprefix ./,$(PKGS))) + $(GO) test -count=1 $(GO_TEST_EXTRA_ARGS) $(addprefix ./,$(PKGS))) # integration is a shorthand for running 'make' in the 't' directory. .PHONY : integration @@ -469,6 +469,7 @@ man/git-lfs-migrate.1 \ man/git-lfs-pointer.1 \ man/git-lfs-post-checkout.1 \ + man/git-lfs-post-commit.1 \ man/git-lfs-post-merge.1 \ man/git-lfs-pre-push.1 \ man/git-lfs-prune.1 \ @@ -501,6 +502,7 @@ man/git-lfs-migrate.1.html \ man/git-lfs-pointer.1.html \ man/git-lfs-post-checkout.1.html \ + man/git-lfs-post-commit.1.html \ man/git-lfs-post-merge.1.html \ man/git-lfs-pre-push.1.html \ man/git-lfs-prune.1.html \ diff -Nru git-lfs-2.10.0/README.md git-lfs-2.11.0/README.md --- git-lfs-2.10.0/README.md 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/README.md 2020-05-08 16:06:35.000000000 +0000 @@ -57,6 +57,23 @@ $ git lfs install ``` +#### Verifying releases + +Releases are signed with the OpenPGP key of one of the core team members. To +get these keys, you can run the following command, which will print them to +standard output: + +```ShellSession +$ curl -L https://api.github.com/repos/git-lfs/git-lfs/tarball/core-gpg-keys | tar -Ozxf - +``` + +Once you have the keys, you can download the `sha256sums.asc` file and verify +the file you want like so: + +```ShellSession +$ gpg -d sha256sums.asc | grep git-lfs-linux-amd64-v2.10.0.tar.gz | shasum -a 256 -c +``` + ## Example Usage To begin using Git LFS within a Git repository that is not already configured diff -Nru git-lfs-2.10.0/rpm/build_rpms.bsh git-lfs-2.11.0/rpm/build_rpms.bsh --- git-lfs-2.10.0/rpm/build_rpms.bsh 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/rpm/build_rpms.bsh 2020-05-08 16:06:35.000000000 +0000 @@ -114,6 +114,7 @@ "${RPMBUILD[@]}" -ba ${CURDIR}/SPECS/ruby.spec echo "Installing ruby..." $SUDO yum install -y --nogpgcheck ${CURDIR}/RPMS/x86_64/ruby*.rpm + $SUDO yum erase -y libyaml-devel autoconf gcc-c++ readline-devel zlib-devel openssl-devel automake libtool sqlite-devel else $SUDO yum install -y ruby ruby-devel fi @@ -142,6 +143,9 @@ $SUDO yum install -y --nogpgcheck $(ls ${CURDIR}/RPMS/noarch/rubygem-*.rpm ${CURDIR}/RPMS/x86_64/rubygem-*.rpm | grep -v debuginfo) fi +rm -fr ${CURDIR}/{BUILD,BUILDROOT} +mkdir -p ${CURDIR}/{BUILD,BUILDROOT} + pushd ${CURDIR}/.. #Yes, compile lfs before compiling lfs... make diff -Nru git-lfs-2.10.0/rpm/SPECS/git-lfs.spec git-lfs-2.11.0/rpm/SPECS/git-lfs.spec --- git-lfs-2.10.0/rpm/SPECS/git-lfs.spec 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/rpm/SPECS/git-lfs.spec 2020-05-08 16:06:35.000000000 +0000 @@ -1,5 +1,5 @@ Name: git-lfs -Version: 2.10.0 +Version: 2.11.0 Release: 1%{?dist} Summary: Git extension for versioning large files diff -Nru git-lfs-2.10.0/script/build-git git-lfs-2.11.0/script/build-git --- git-lfs-2.10.0/script/build-git 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/script/build-git 2020-05-08 16:06:35.000000000 +0000 @@ -4,9 +4,9 @@ case $(uname -s) in Darwin) - brew install curl zlib pcre2 gettext openssl - brew reinstall curl zlib pcre2 gettext openssl - for i in curl zlib pcre2 gettext openssl + brew install curl zlib pcre2 openssl + brew reinstall curl zlib pcre2 openssl + for i in curl zlib pcre2 openssl do brew unlink $i || true; brew link --force $i || true @@ -21,6 +21,7 @@ cd "$DIR" printf "%s\n" \ + "NO_GETTEXT=YesPlease" \ "NO_OPENSSL=YesPlease" \ "prefix=/usr/local" \ > config.mak diff -Nru git-lfs-2.10.0/script/packagecloud.rb git-lfs-2.11.0/script/packagecloud.rb --- git-lfs-2.10.0/script/packagecloud.rb 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/script/packagecloud.rb 2020-05-08 16:06:35.000000000 +0000 @@ -27,9 +27,6 @@ # https://packagecloud.io/docs#os_distro_version $distro_name_map = { # RHEL EOL https://access.redhat.com/support/policy/updates/errata - "centos/5" => [ - "el/5" # End of Extended Support November 30, 2020 - ], "centos/6" => [ "el/6", # End of Extended Support June 30, 2024 "scientific/6", @@ -37,21 +34,15 @@ "centos/7" => [ "el/7", "scientific/7", - #"el/8", # BOL ~2019-2020? # Fedora EOL check https://fedoraproject.org/wiki/End_of_life # or https://en.wikipedia.org/wiki/Fedora_version_history#Version_history - "fedora/28", # EOL ~Oct 2019 "fedora/29", # EOL ~2020 "fedora/30", # EOL ~2020 - # "fedora/30", # BOL ~May 2019 # opensuse https://en.opensuse.org/Lifetime # or https://en.wikipedia.org/wiki/OpenSUSE_version_history - "opensuse/42.3", # EOL 2019-06-30 - "opensuse/15.0", # EOL 2019-11-25 "opensuse/15.1", # EOL 2020-11 # SLES EOL https://www.suse.com/lifecycle/ "sles/11.4", # LTSS ends 31 Mar 2022 - "sles/12.0", # LTSS ends 01 July 2019 "sles/12.1", # LTSS ends 31 May 2020 "sles/12.2", # LTSS ends 31 Mar 2021 "sles/12.3", # LTSS ends 30 Jun 2022 @@ -59,19 +50,14 @@ ], "centos/8" => [ "el/8", + "fedora/31", # EOL ~2021 ], # Debian EOL https://wiki.debian.org/LTS/ # Ubuntu EOL https://wiki.ubuntu.com/Releases # Mint EOL https://linuxmint.com/download_all.php "debian/8" => [ "debian/jessie", # EOL June 30, 2020 - "linuxmint/qiana", # EOL April 2019 - "linuxmint/rafaela", # EOL April 2019 - "linuxmint/rebecca", # EOL April 2019 - "linuxmint/rosa", # EOL April 2019 "ubuntu/trusty", # ESM April 2022 - "ubuntu/vivid", # EOL February 4, 2016 - "ubuntu/wily" # EOL July 28, 2016 ], "debian/9" => [ "debian/stretch", # EOL June 2022 @@ -81,17 +67,16 @@ "linuxmint/sylvia", # EOL April 2021 "linuxmint/tara", # EOL April 2023 "linuxmint/tessa", # EOL April 2023 + "linuxmint/tina", # EOL April 2023 + "linuxmint/tricia", # EOL April 2023 "ubuntu/xenial", # ESM April 2024 - "ubuntu/yakkety", # EOL July 20, 2017 - "ubuntu/zesty", # EOL January 13, 2018 - "ubuntu/artful", # EOL July 19 2018 "ubuntu/bionic", # ESM April 2028 - "ubuntu/cosmic", # EOL July 2019 "ubuntu/disco", # EOL April 2020 ], "debian/10" => [ "debian/buster", # Current - "ubuntu/eoan", # Current + "ubuntu/eoan", # EOL July 2020 + "ubuntu/focal", # Current ] } diff -Nru git-lfs-2.10.0/subprocess/subprocess.go git-lfs-2.11.0/subprocess/subprocess.go --- git-lfs-2.10.0/subprocess/subprocess.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/subprocess/subprocess.go 2020-05-08 16:06:35.000000000 +0000 @@ -127,16 +127,24 @@ return strings.Join(quoted, " ") } -// An env for an exec.Command without GIT_TRACE +// An env for an exec.Command without GIT_TRACE and GIT_INTERNAL_SUPER_PREFIX var env []string var traceEnv = "GIT_TRACE=" +// Don't pass GIT_INTERNAL_SUPER_PREFIX back to Git. Git passes this environment +// variable to child processes when submodule.recurse is set to true. However, +// passing that environment variable back to Git will cause it to append the +// --super-prefix command-line option to every Git call. This is problematic +// because many Git commands (including git config and git rev-parse) don't +// support --super-prefix and would immediately exit with an error as a result. +var superPrefixEnv = "GIT_INTERNAL_SUPER_PREFIX=" + func init() { realEnv := os.Environ() env = make([]string, 0, len(realEnv)) for _, kv := range realEnv { - if strings.HasPrefix(kv, traceEnv) { + if strings.HasPrefix(kv, traceEnv) || strings.HasPrefix(kv, superPrefixEnv) { continue } env = append(env, kv) diff -Nru git-lfs-2.10.0/t/cmd/lfstest-gitserver.go git-lfs-2.11.0/t/cmd/lfstest-gitserver.go --- git-lfs-2.10.0/t/cmd/lfstest-gitserver.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/t/cmd/lfstest-gitserver.go 2020-05-08 16:06:35.000000000 +0000 @@ -59,7 +59,7 @@ "status-storage-403", "status-storage-404", "status-storage-410", "status-storage-422", "status-storage-500", "status-storage-503", "status-batch-resume-206", "batch-resume-fail-fallback", "return-expired-action", "return-expired-action-forever", "return-invalid-size", "object-authenticated", "storage-download-retry", "storage-upload-retry", "storage-upload-retry-later", "unknown-oid", - "send-verify-action", "send-deprecated-links", + "send-verify-action", "send-deprecated-links", "redirect-storage-upload", } reqCookieReposRE = regexp.MustCompile(`\A/require-cookie-`) @@ -258,7 +258,10 @@ } } -func lfsUrl(repo, oid string) string { +func lfsUrl(repo, oid string, redirect bool) string { + if redirect { + return server.URL + "/redirect307/objects/" + oid + "?r=" + repo + } return server.URL + "/storage/" + oid + "?r=" + repo } @@ -488,10 +491,9 @@ if handler == "send-deprecated-links" { o.Links = make(map[string]*lfsLink) } - if addAction { a := &lfsLink{ - Href: lfsUrl(repo, obj.Oid), + Href: lfsUrl(repo, obj.Oid, handler == "redirect-storage-upload"), Header: map[string]string{}, } a = serveExpired(a, repo, handler) @@ -644,7 +646,6 @@ if !ok { return } - repo := r.URL.Query().Get("r") parts := strings.Split(r.URL.Path, "/") oid := parts[len(parts)-1] @@ -975,6 +976,9 @@ redirectTo = "/" + strings.Join(parts[3:], "/") } else if parts[2] == "abs" { redirectTo = server.URL + "/" + strings.Join(parts[3:], "/") + } else if parts[2] == "objects" { + repo := r.URL.Query().Get("r") + redirectTo = server.URL + "/storage/" + strings.Join(parts[3:], "/") + "?r=" + repo } else { debug(id, "Invalid URL for redirect: %v", r.URL) w.WriteHeader(404) diff -Nru git-lfs-2.10.0/t/cmd/util/testutils.go git-lfs-2.11.0/t/cmd/util/testutils.go --- git-lfs-2.10.0/t/cmd/util/testutils.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/t/cmd/util/testutils.go 2020-05-08 16:06:35.000000000 +0000 @@ -16,6 +16,7 @@ "os" "os/exec" "path/filepath" + "runtime" "strings" "sync" "time" @@ -27,6 +28,34 @@ "github.com/git-lfs/git-lfs/lfs" ) +func init() { + path := os.Getenv("PATH") + sep := "" + if path != "" { + if runtime.GOOS == "windows" { + sep = ";" + } else { + sep = ":" + } + } + + // Strip the trailing "t/cmd/util/testutils.go" from the path to this + // source file to create a path to the working tree's "bin" directory, + // then prepend that to the PATH environment variable to ensure our + // "git-lfs" binary is used in preference to any installed versions when + // executing the Go tests. + _, srcdir, _, _ := runtime.Caller(0) + for i := 0; i < 4; i++ { + srcdir = filepath.Dir(srcdir) + } + var err error + srcdir, err = filepath.Abs(srcdir) + if err != nil { + panic(err) + } + os.Setenv("PATH", filepath.Join(srcdir, "bin")+sep+path) +} + type RepoType int const ( diff -Nru git-lfs-2.10.0/t/git-lfs-test-server-api/main.go git-lfs-2.11.0/t/git-lfs-test-server-api/main.go --- git-lfs-2.10.0/t/git-lfs-test-server-api/main.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/t/git-lfs-test-server-api/main.go 2020-05-08 16:06:35.000000000 +0000 @@ -206,7 +206,7 @@ if err != nil { return nil, nil, err } - uploadQueue.Add(t.Name, t.Path, t.Oid, t.Size, false) + uploadQueue.Add(t.Name, t.Path, t.Oid, t.Size, false, nil) } uploadQueue.Wait() diff -Nru git-lfs-2.10.0/t/t-cherry-pick-commits.sh git-lfs-2.11.0/t/t-cherry-pick-commits.sh --- git-lfs-2.10.0/t/t-cherry-pick-commits.sh 1970-01-01 00:00:00.000000000 +0000 +++ git-lfs-2.11.0/t/t-cherry-pick-commits.sh 2020-05-08 16:06:35.000000000 +0000 @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +. "$(dirname "$0")/testlib.sh" + +begin_test "cherry-pick two commits without lfs cache" +( + set -e + + reponame="$(basename "$0" ".sh")-cherry-pick-commits" + setup_remote_repo "$reponame" + clone_repo "$reponame" cherrypickcommits + + git lfs track "*.dat" + git add .gitattributes + git commit -m "initial commit" + + git branch secondbranch + + echo "smudge a" > a.dat + git add a.dat + git commit -m "add a.dat" + commit1=$(git log -n1 --format="%H") + + echo "smudge b" > b.dat + git add b.dat + git commit -m "add a.dat" + commit2=$(git log -n1 --format="%H") + + git push origin master + + git checkout secondbranch + rm -rf .git/lfs/objects + + git cherry-pick $commit1 $commit2 +) +end_test diff -Nru git-lfs-2.10.0/t/t-dedup.sh git-lfs-2.11.0/t/t-dedup.sh --- git-lfs-2.10.0/t/t-dedup.sh 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/t/t-dedup.sh 2020-05-08 16:06:35.000000000 +0000 @@ -10,6 +10,21 @@ git init $reponame cd $reponame + # Confirm Git LFS extensions prevent de-duplication + git config lfs.extension.foo.clean "foo-clean %f" + git config lfs.extension.foo.smudge "foo-smudge %f" + git config lfs.extension.foo.priority 0 + + result=$(git lfs dedup 2>&1) && true + if ( echo $result | grep "This system does not support deduplication." ); then + exit + fi + echo "$result" | grep 'This platform supports file de-duplication, however, Git LFS extensions are configured and therefore de-duplication can not be used.' + + git config --unset lfs.extension.foo.clean + git config --unset lfs.extension.foo.smudge + git config --unset lfs.extension.foo.priority + # Create a commit with some files tracked by git-lfs git lfs track *.dat echo "test data" > a.dat @@ -25,9 +40,6 @@ # DO result=$(git lfs dedup 2>&1) && true - if ( echo $result | grep "This system does not support deduplication." ); then - exit - fi # VERIFY: Expected # Success: a.dat @@ -46,11 +58,23 @@ git init $reponame cd $reponame - # DO + # Confirm Git LFS extensions prevent de-duplication + git config lfs.extension.foo.clean "foo-clean %f" + git config lfs.extension.foo.smudge "foo-smudge %f" + git config lfs.extension.foo.priority 0 + result=$(git lfs dedup --test 2>&1) && true if ( echo $result | grep "This system does not support deduplication." ); then exit fi + echo "$result" | grep 'This platform supports file de-duplication, however, Git LFS extensions are configured and therefore de-duplication can not be used.' + + git config --unset lfs.extension.foo.clean + git config --unset lfs.extension.foo.smudge + git config --unset lfs.extension.foo.priority + + # DO + result=$(git lfs dedup --test 2>&1) && true # Verify: This platform and repository support file de-duplication. echo "$result" | grep 'This platform and repository support file de-duplication.' diff -Nru git-lfs-2.10.0/t/t-fetch.sh git-lfs-2.11.0/t/t-fetch.sh --- git-lfs-2.10.0/t/t-fetch.sh 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/t/t-fetch.sh 2020-05-08 16:06:35.000000000 +0000 @@ -619,3 +619,23 @@ grep "Invalid remote name" fetch.log ) end_test + +begin_test "fetch fails when LFS directory has wrong permissions" +( + set -e + + # Windows lacks POSIX permissions. + [ "$IS_WINDOWS" -eq 1 ] && exit 0 + + # Root is exempt from permissions. + [ "$(id -u)" -eq 0 ] && exit 0 + + cd shared + rm -rf .git/lfs/objects + mkdir .git/lfs/objects + chmod 400 .git/lfs/objects + + git lfs fetch 2>&1 | tee fetch.log + grep "error trying to create local storage directory" fetch.log +) +end_test diff -Nru git-lfs-2.10.0/t/t-ls-files.sh git-lfs-2.11.0/t/t-ls-files.sh --- git-lfs-2.10.0/t/t-ls-files.sh 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/t/t-ls-files.sh 2020-05-08 16:06:35.000000000 +0000 @@ -497,4 +497,22 @@ [ 0 -eq $(grep -c "b\.dat" ls-files.log) ] [ 0 -eq $(grep -c "c\.dat" ls-files.log) ] ) +end_test + +begin_test "ls-files: not affected by lfs.fetchexclude" +( + set -e + + mkdir repo-fetchexclude + cd repo-fetchexclude + git init + git lfs track "*.dat" | grep "Tracking \"\*.dat\"" + echo "some data" > some.dat + echo "some text" > some.txt + echo "missing" > missing.dat + git add missing.dat + git commit -m "add missing file" + git config lfs.fetchexclude '*' + [ "6bbd052ab0 * missing.dat" = "$(git lfs ls-files)" ] +) end_test \ No newline at end of file diff -Nru git-lfs-2.10.0/t/t-migrate-fixup.sh git-lfs-2.11.0/t/t-migrate-fixup.sh --- git-lfs-2.10.0/t/t-migrate-fixup.sh 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/t/t-migrate-fixup.sh 2020-05-08 16:06:35.000000000 +0000 @@ -100,3 +100,32 @@ grep -q "fatal: --no-rewrite and --fixup cannot be combined" migrate.log ) end_test + +begin_test "migrate import (--fixup with remote tags)" +( + set -e + + setup_single_local_branch_tracked_corrupt + + git lfs uninstall + + base64 < /dev/urandom | head -c 120 > b.txt + git add b.txt + git commit -m "b.txt" + + git tag -m tag1 -a tag1 + git reset --hard HEAD^ + + git lfs install + + cwd=$(pwd) + cd "$TRASHDIR" + + git clone "$cwd" "$reponame-2" + cd "$reponame-2" + + # We're checking here that this succeeds even though it does nothing in this + # case. + git lfs migrate import --fixup --yes master +) +end_test diff -Nru git-lfs-2.10.0/t/t-migrate-import.sh git-lfs-2.11.0/t/t-migrate-import.sh --- git-lfs-2.10.0/t/t-migrate-import.sh 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/t/t-migrate-import.sh 2020-05-08 16:06:35.000000000 +0000 @@ -439,6 +439,38 @@ ) end_test +begin_test "migrate import (--exclude with existing .gitattributes)" +( + set -e + + setup_local_branch_with_gitattrs + + pwd + + master="$(git rev-parse refs/heads/master)" + + txt_master_oid="$(calc_oid "$(git cat-file -p "$master:a.txt")")" + + git lfs migrate import --yes --include-ref=refs/heads/master --include="*.txt" --exclude="*.bin" + + assert_local_object "$txt_master_oid" "120" + + master="$(git rev-parse refs/heads/master)" + prev="$(git rev-parse refs/heads/master^1)" + + diff -u <(git cat-file -p $master:.gitattributes) <(cat <<-EOF +*.txt filter=lfs diff=lfs merge=lfs -text +*.other filter=lfs diff=lfs merge=lfs -text +*.bin !text -filter -merge -diff +EOF) + + diff -u <(git cat-file -p $prev:.gitattributes) <(cat <<-EOF +*.txt filter=lfs diff=lfs merge=lfs -text +*.bin !text -filter -merge -diff +EOF) +) +end_test + begin_test "migrate import (identical contents, different permissions)" ( set -e diff -Nru git-lfs-2.10.0/t/t-pre-push.sh git-lfs-2.11.0/t/t-pre-push.sh --- git-lfs-2.10.0/t/t-pre-push.sh 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/t/t-pre-push.sh 2020-05-08 16:06:35.000000000 +0000 @@ -1181,6 +1181,28 @@ ) end_test +begin_test "pre-push uses optimization if remote URL matches" +( + set -e + reponame="pre-push-remote-url-optimization" + setup_remote_repo "$reponame" + clone_repo "$reponame" "$reponame" + + endpoint=$(git config remote.origin.url) + contents_oid=$(calc_oid 'hi\n') + git config "lfs.$endpoint.locksverify" false + git lfs track "*.dat" + echo "hi" > a.dat + git add .gitattributes a.dat + git commit -m "add a.dat" + + refute_server_object "$reponame" $contents_oid "refs/heads/master" + + GIT_TRACE=1 GIT_TRANSFER_TRACE=1 git push "$endpoint" master 2>&1 | tee push.log + grep 'rev-list.*--not --remotes=origin' push.log +) +end_test + begin_test "pre-push does not traverse Git objects server has" ( set -e @@ -1198,10 +1220,10 @@ refute_server_object "$reponame" $contents_oid "refs/heads/master" - # We use a URL instead of a named remote so that we can't make use of the - # optimization that ignores objects we already have in remote tracking - # branches. - GIT_TRACE=1 GIT_TRANSFER_TRACE=1 git push "$endpoint" master 2>&1 | tee push.log + # We use a different URL instead of a named remote or the remote URL so that + # we can't make use of the optimization that ignores objects we already have + # in remote tracking branches. + GIT_TRACE=1 GIT_TRANSFER_TRACE=1 git push "$endpoint.git" master 2>&1 | tee push.log assert_server_object "$reponame" $contents_oid "refs/heads/master" @@ -1212,7 +1234,7 @@ refute_server_object "$reponame" $contents2_oid "refs/heads/master" - GIT_TRACE=1 GIT_TRANSFER_TRACE=1 git push "$endpoint" master 2>&1 | tee push.log + GIT_TRACE=1 GIT_TRANSFER_TRACE=1 git push "$endpoint.git" master 2>&1 | tee push.log assert_server_object "$reponame" $contents2_oid "refs/heads/master" @@ -1222,3 +1244,61 @@ ! grep $contents_oid push.log ) end_test + +begin_test "pre-push with force-pushed ref" +( + set -e + reponame="pre-push-force-pushed-ref" + setup_remote_repo "$reponame" + clone_repo "$reponame" "$reponame" + + git config "lfs.$(repo_endpoint "$GITSERVER" "$reponame").locksverify" false + git lfs track "*.dat" + echo "hi" > a.dat + git add .gitattributes a.dat + git commit -m "add a.dat" + git tag -a -m tagname tagname + + refute_server_object "$reponame" 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4 "refs/heads/master" + + git push origin master tagname + + assert_server_object "$reponame" 98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4 "refs/heads/master" + + # We pick a different message so that we get different object IDs even if both + # commands run in the same second. + git tag -f -a -m tagname2 tagname + # Prune the old tag object. + git reflog expire --all --expire=now + git gc --prune=now + # Make sure we deal with us missing the object for the old value of the tag ref. + git push origin +tagname +) +end_test + +begin_test "pre-push with local path" +( + set -e + reponame="pre-push-local-path" + setup_remote_repo "$reponame" + clone_repo "$reponame" "$reponame-2" + cd .. + clone_repo "$reponame" "$reponame" + + git lfs track "*.dat" + echo "hi" > a.dat + git add .gitattributes a.dat + git commit -m "add a.dat" + + # Push to the other repo. + git push "../$reponame-2" master:foo + + # Push to . to make sure that works. + git push "." master:foo + + git lfs fsck + cd "../$reponame-2" + git checkout foo + git lfs fsck +) +end_test diff -Nru git-lfs-2.10.0/t/t-push.sh git-lfs-2.11.0/t/t-push.sh --- git-lfs-2.10.0/t/t-push.sh 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/t/t-push.sh 2020-05-08 16:06:35.000000000 +0000 @@ -575,7 +575,7 @@ GIT_TRACE=1 git push origin master 2>&1 | tee push.log - expected="enqueue retry #1 for \"$contents_oid\" (size: $contents_size): LFS: tq: action \"upload\" expires at" + expected="enqueue retry #1 after 0.25s for \"$contents_oid\" (size: $contents_size): LFS: tq: action \"upload\" expires at" grep "$expected" push.log grep "Uploading LFS objects: 100% (1/1), 21 B" push.log diff -Nru git-lfs-2.10.0/t/t-submodule-recurse.sh git-lfs-2.11.0/t/t-submodule-recurse.sh --- git-lfs-2.10.0/t/t-submodule-recurse.sh 1970-01-01 00:00:00.000000000 +0000 +++ git-lfs-2.11.0/t/t-submodule-recurse.sh 2020-05-08 16:06:35.000000000 +0000 @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +. "$(dirname "$0")/testlib.sh" +reponame="submodule-recurse-test-repo" +submodname="submodule-recurse-test-submodule" + +begin_test "submodule with submodule.recurse = true" +( + set -e + + setup_remote_repo "$reponame" + setup_remote_repo "$submodname" + + clone_repo "$submodname" submodule + + git lfs track "*.dat" 2>&1 | tee track.log + grep "Tracking \"\*.dat\"" track.log + + echo "foo" > file.dat + git add .gitattributes file.dat + git commit -a -m "add file" + git push origin master + subcommit1=$(git rev-parse HEAD) + + echo "bar" > file.dat + git add file.dat + git commit -a -m "update file" + git push origin master + subcommit2=$(git rev-parse HEAD) + + clone_repo "$reponame" repo + git submodule add "$GITSERVER/$submodname" submodule + git submodule update --init --recursive + git -C submodule reset --hard "$subcommit1" + git add .gitmodules submodule + git commit -m "add submodule" + git push origin master + + git checkout -b feature + git -C submodule reset --hard "$subcommit2" + git add .gitmodules submodule + git commit -m "update submodule" + git push origin feature + + clone_repo "$reponame" repo-no-recurse + git submodule update --init --recursive + git checkout feature + + if [[ -d "submodule/lfs/logs" ]] + then + exit 1 + fi + + clone_repo "$reponame" repo-recurse + git config submodule.recurse true + git submodule update --init --recursive + git checkout feature + + if [[ -d "submodule/lfs/logs" ]] + then + exit 1 + fi +) +end_test diff -Nru git-lfs-2.10.0/t/t-track.sh git-lfs-2.11.0/t/t-track.sh --- git-lfs-2.10.0/t/t-track.sh 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/t/t-track.sh 2020-05-08 16:06:35.000000000 +0000 @@ -670,6 +670,7 @@ contents_oid=$(calc_oid "$contents") git lfs track --filename "$filename" + git lfs track --filename "$filename" | grep 'already supported' git add . cat .gitattributes diff -Nru git-lfs-2.10.0/t/t-upload-redirect.sh git-lfs-2.11.0/t/t-upload-redirect.sh --- git-lfs-2.10.0/t/t-upload-redirect.sh 1970-01-01 00:00:00.000000000 +0000 +++ git-lfs-2.11.0/t/t-upload-redirect.sh 2020-05-08 16:06:35.000000000 +0000 @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +. "$(dirname "$0")/testlib.sh" + +begin_test "redirect upload" +( + set -e + + reponame="redirect-storage-upload" + setup_remote_repo "$reponame" + clone_repo "$reponame" redirect-repo-upload + + contents="redirect-storage-upload" + oid="$(calc_oid "$contents")" + printf "%s" "$contents" > a.dat + + git lfs track "*.dat" + git add .gitattributes a.dat + git commit -m "initial commit" + + GIT_TRACE=1 git push origin master 2>&1 | tee push.log + if [ "0" -ne "${PIPESTATUS[0]}" ]; then + echo >&2 "fatal: expected \`git push origin master\` to succeed ..." + exit 1 + fi + + grep "api: redirect" push.log + + assert_server_object "$reponame" "$oid" +) +end_test diff -Nru git-lfs-2.10.0/tools/copycallback.go git-lfs-2.11.0/tools/copycallback.go --- git-lfs-2.10.0/tools/copycallback.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/tools/copycallback.go 2020-05-08 16:06:35.000000000 +0000 @@ -3,6 +3,7 @@ import ( "bytes" "io" + "os" ) type CopyCallback func(totalSize int64, readSoFar int64, readSinceLast int) error @@ -18,6 +19,10 @@ return NewBodyWithCallback(NewByteBody(by), totalSize, cb) } +func NewFileBodyWithCallback(f *os.File, totalSize int64, cb CopyCallback) *BodyWithCallback { + return NewBodyWithCallback(NewFileBody(f), totalSize, cb) +} + func NewBodyWithCallback(body ReadSeekCloser, totalSize int64, cb CopyCallback) *BodyWithCallback { return &BodyWithCallback{ c: cb, @@ -99,3 +104,15 @@ func (r *closingByteReader) Close() error { return nil } + +func NewFileBody(f *os.File) ReadSeekCloser { + return &closingFileReader{File: f} +} + +type closingFileReader struct { + *os.File +} + +func (r *closingFileReader) Close() error { + return nil +} diff -Nru git-lfs-2.10.0/tools/util_generic.go git-lfs-2.11.0/tools/util_generic.go --- git-lfs-2.10.0/tools/util_generic.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/tools/util_generic.go 2020-05-08 16:06:35.000000000 +0000 @@ -1,4 +1,4 @@ -// +build !linux !cgo +// +build !linux // +build !darwin !cgo // +build !windows diff -Nru git-lfs-2.10.0/tools/util_linux.go git-lfs-2.11.0/tools/util_linux.go --- git-lfs-2.10.0/tools/util_linux.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/tools/util_linux.go 2020-05-08 16:06:35.000000000 +0000 @@ -1,17 +1,7 @@ -// +build linux,cgo +// +build linux package tools -/* -#include - -#undef FICLONE -#define FICLONE _IOW(0x94, 9, int) -// copy from https://github.com/torvalds/linux/blob/v5.2/include/uapi/linux/fs.h#L195 for older header files. -// This is equal to the older BTRFS_IOC_CLONE value. -*/ -import "C" - import ( "io" "io/ioutil" @@ -20,7 +10,10 @@ ) const ( - ioctlFiClone = C.FICLONE + // This is the FICLONE ioctl value found in linux/fs.h on a typical + // system. It's equivalent to the older BTRFS_IOC_CLONE. We hard-code + // the value here to avoid a dependency on cgo. + ioctlFiClone = 0x40049409 ) // CheckCloneFileSupported runs explicit test of clone file on supplied directory. diff -Nru git-lfs-2.10.0/tq/basic_upload.go git-lfs-2.11.0/tq/basic_upload.go --- git-lfs-2.10.0/tq/basic_upload.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/tq/basic_upload.go 2020-05-08 16:06:35.000000000 +0000 @@ -86,7 +86,7 @@ return nil } - cbr := tools.NewBodyWithCallback(f, t.Size, ccb) + cbr := tools.NewFileBodyWithCallback(f, t.Size, ccb) var reader lfsapi.ReadSeekCloser = cbr // Signal auth was ok on first read; this frees up other workers to start diff -Nru git-lfs-2.10.0/tq/manifest.go git-lfs-2.11.0/tq/manifest.go --- git-lfs-2.10.0/tq/manifest.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/tq/manifest.go 2020-05-08 16:06:35.000000000 +0000 @@ -12,13 +12,16 @@ const ( defaultMaxRetries = 8 + defaultMaxRetryDelay = 10 defaultConcurrentTransfers = 8 ) type Manifest struct { // maxRetries is the maximum number of retries a single object can - // attempt to make before it will be dropped. + // attempt to make before it will be dropped. maxRetryDelay is the maximum + // time in seconds to wait between retry attempts when using backoff. maxRetries int + maxRetryDelay int concurrentTransfers int basicTransfersOnly bool standaloneTransferAgent string @@ -39,6 +42,10 @@ return m.maxRetries } +func (m *Manifest) MaxRetryDelay() int { + return m.maxRetryDelay +} + func (m *Manifest) ConcurrentTransfers() int { return m.concurrentTransfers } @@ -77,6 +84,9 @@ if v := git.Int("lfs.transfer.maxretries", 0); v > 0 { m.maxRetries = v } + if v := git.Int("lfs.transfer.maxretrydelay", -1); v > -1 { + m.maxRetryDelay = v + } if v := git.Int("lfs.concurrenttransfers", 0); v > 0 { m.concurrentTransfers = v } @@ -91,6 +101,9 @@ if m.maxRetries < 1 { m.maxRetries = defaultMaxRetries } + if m.maxRetryDelay < 1 { + m.maxRetryDelay = defaultMaxRetryDelay + } if m.concurrentTransfers < 1 { m.concurrentTransfers = defaultConcurrentTransfers diff -Nru git-lfs-2.10.0/tq/transfer_queue.go git-lfs-2.11.0/tq/transfer_queue.go --- git-lfs-2.10.0/tq/transfer_queue.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/tq/transfer_queue.go 2020-05-08 16:06:35.000000000 +0000 @@ -17,10 +17,12 @@ const ( defaultBatchSize = 100 + baseRetryDelayMs = 250 ) type retryCounter struct { - MaxRetries int `git:"lfs.transfer.maxretries"` + MaxRetries int + MaxRetryDelay int // cmu guards count cmu sync.Mutex @@ -28,26 +30,23 @@ count map[string]int } -// newRetryCounter instantiates a new *retryCounter. It parses the gitconfig -// value: `lfs.transfer.maxretries`, and falls back to defaultMaxRetries if none -// was provided. -// -// If it encountered an error in Unmarshaling the *config.Configuration, it will -// be returned, otherwise nil. +// newRetryCounter instantiates a new *retryCounter. func newRetryCounter() *retryCounter { return &retryCounter{ - MaxRetries: defaultMaxRetries, - count: make(map[string]int), + MaxRetries: defaultMaxRetries, + MaxRetryDelay: defaultMaxRetryDelay, + count: make(map[string]int), } } -// Increment increments the number of retries for a given OID. It is safe to -// call across multiple goroutines. -func (r *retryCounter) Increment(oid string) { +// Increment increments the number of retries for a given OID and returns the +// new value. It is safe to call across multiple goroutines. +func (r *retryCounter) Increment(oid string) int { r.cmu.Lock() defer r.cmu.Unlock() r.count[oid]++ + return r.count[oid] } // CountFor returns the current number of retries for a given OID. It is safe to @@ -66,6 +65,22 @@ return count, count < r.MaxRetries } +// ReadyTime returns the time from now when the current retry can occur or the +// zero time if the retry can occur immediately. +func (r *retryCounter) ReadyTime(oid string) time.Time { + count := r.CountFor(oid) + if count < 1 { + return time.Time{} + } + + maxDelayMs := 1000 * uint64(r.MaxRetryDelay) + delay := uint64(baseRetryDelayMs) * (1 << uint(count-1)) + if delay == 0 || delay > maxDelayMs { + delay = maxDelayMs + } + return time.Now().Add(time.Duration(delay) * time.Millisecond) +} + // batch implements the sort.Interface interface and enables sorting on a slice // of `*Transfer`s by object size. // @@ -295,6 +310,7 @@ } q.rc.MaxRetries = q.manifest.maxRetries + q.rc.MaxRetryDelay = q.manifest.maxRetryDelay q.client.MaxRetries = q.manifest.maxRetries if q.batchSize <= 0 { @@ -324,7 +340,12 @@ // // Only one file will be transferred to/from the Path element of the first // transfer. -func (q *TransferQueue) Add(name, path, oid string, size int64, missing bool) { +func (q *TransferQueue) Add(name, path, oid string, size int64, missing bool, err error) { + if err != nil { + q.errorc <- err + return + } + t := &objectTuple{ Name: name, Path: path, @@ -501,6 +522,24 @@ next := q.makeBatch() tracerx.Printf("tq: sending batch of size %d", len(batch)) + enqueueRetry := func(t *objectTuple, err error, readyTime *time.Time) { + count := q.rc.Increment(t.Oid) + + if readyTime == nil { + t.ReadyTime = q.rc.ReadyTime(t.Oid) + } else { + t.ReadyTime = *readyTime + } + delay := time.Until(t.ReadyTime).Seconds() + + var errMsg string + if err != nil { + errMsg = fmt.Sprintf(": %s", err) + } + tracerx.Printf("tq: enqueue retry #%d after %.2fs for %q (size: %d)%s", count, delay, t.Oid, t.Size, errMsg) + next = append(next, t) + } + q.meter.Pause() var bRes *BatchResponse if q.manifest.standaloneTransferAgent != "" { @@ -525,14 +564,10 @@ // retried, they will be marked as failed. for _, t := range batch { if q.canRetryObject(t.Oid, err) { - q.rc.Increment(t.Oid) - - next = append(next, t) + enqueueRetry(t, err, nil) } else if readyTime, canRetry := q.canRetryObjectLater(t.Oid, err); canRetry { - tracerx.Printf("tq: retrying object %s after %s seconds.", t.Oid, time.Until(readyTime).Seconds()) err = nil - t.ReadyTime = readyTime - next = append(next, t) + enqueueRetry(t, err, &readyTime) } else { q.wait.Done() } @@ -594,13 +629,8 @@ tr := newTransfer(o, objects.First().Name, objects.First().Path) if a, err := tr.Rel(q.direction.String()); err != nil { - // XXX(taylor): duplication if q.canRetryObject(tr.Oid, err) { - q.rc.Increment(tr.Oid) - count := q.rc.CountFor(tr.Oid) - - tracerx.Printf("tq: enqueue retry #%d for %q (size: %d): %s", count, tr.Oid, tr.Size, err) - next = append(next, objects.First()) + enqueueRetry(objects.First(), err, nil) } else { q.errorc <- errors.Errorf("[%v] %v", tr.Name, err) @@ -619,12 +649,7 @@ retries := q.addToAdapter(bRes.endpoint, toTransfer) for t := range retries { - q.rc.Increment(t.Oid) - count := q.rc.CountFor(t.Oid) - - tracerx.Printf("tq: enqueue retry #%d for %q (size: %d)", count, t.Oid, t.Size) - - next = append(next, t) + enqueueRetry(t, nil, nil) } return next, nil diff -Nru git-lfs-2.10.0/tq/transfer_queue_test.go git-lfs-2.11.0/tq/transfer_queue_test.go --- git-lfs-2.10.0/tq/transfer_queue_test.go 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/tq/transfer_queue_test.go 2020-05-08 16:06:35.000000000 +0000 @@ -2,6 +2,7 @@ import ( "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -10,15 +11,27 @@ assert.Equal(t, 8, NewManifest(nil, nil, "", "").MaxRetries()) } +func TestManifestDefaultsToFixedRetryDelay(t *testing.T) { + assert.Equal(t, 10, NewManifest(nil, nil, "", "").MaxRetryDelay()) +} + func TestRetryCounterDefaultsToFixedRetries(t *testing.T) { rc := newRetryCounter() assert.Equal(t, 8, rc.MaxRetries) } +func TestRetryCounterDefaultsToFixedRetryDelay(t *testing.T) { + rc := newRetryCounter() + assert.Equal(t, 10, rc.MaxRetryDelay) +} + func TestRetryCounterIncrementsObjects(t *testing.T) { rc := newRetryCounter() - rc.Increment("oid") + assert.Equal(t, 1, rc.Increment("oid")) assert.Equal(t, 1, rc.CountFor("oid")) + + assert.Equal(t, 2, rc.Increment("oid")) + assert.Equal(t, 2, rc.CountFor("oid")) } func TestRetryCounterCanNotRetryAfterExceedingRetryCount(t *testing.T) { @@ -31,6 +44,36 @@ assert.False(t, canRetry) } +func TestRetryCounterDoesNotDelayFirstAttempt(t *testing.T) { + rc := newRetryCounter() + assert.Equal(t, time.Time{}, rc.ReadyTime("oid")) +} + +func TestRetryCounterDelaysExponentially(t *testing.T) { + rc := newRetryCounter() + start := time.Now() + + rc.Increment("oid") + ready1 := rc.ReadyTime("oid") + assert.GreaterOrEqual(t, int64(ready1.Sub(start)/time.Millisecond), int64(baseRetryDelayMs)) + + rc.Increment("oid") + ready2 := rc.ReadyTime("oid") + assert.GreaterOrEqual(t, int64(ready2.Sub(start)/time.Millisecond), int64(2*baseRetryDelayMs)) +} + +func TestRetryCounterLimitsDelay(t *testing.T) { + rc := newRetryCounter() + rc.MaxRetryDelay = 1 + + for i := 0; i < 4; i++ { + rc.Increment("oid") + } + + rt := rc.ReadyTime("oid") + assert.WithinDuration(t, time.Now(), rt, 1*time.Second) +} + func TestBatchSizeReturnsBatchSize(t *testing.T) { q := NewTransferQueue( Upload, NewManifest(nil, nil, "", ""), "origin", WithBatchSize(3)) diff -Nru git-lfs-2.10.0/versioninfo.json git-lfs-2.11.0/versioninfo.json --- git-lfs-2.10.0/versioninfo.json 2020-01-21 17:29:29.000000000 +0000 +++ git-lfs-2.11.0/versioninfo.json 2020-05-08 16:06:35.000000000 +0000 @@ -3,7 +3,7 @@ { "FileVersion": { "Major": 2, - "Minor": 10, + "Minor": 11, "Patch": 0, "Build": 0 } @@ -13,7 +13,7 @@ "FileDescription": "Git LFS", "LegalCopyright": "GitHub, Inc. and Git LFS contributors", "ProductName": "Git Large File Storage (LFS)", - "ProductVersion": "2.10.0" + "ProductVersion": "2.11.0" }, "IconPath": "script/windows-installer/git-lfs-logo.ico" }