Imported Upstream version 2.4.1 upstream/2.4.1
authorhyokeun <hyokeun.jeon@samsung.com>
Fri, 21 Dec 2018 05:52:07 +0000 (14:52 +0900)
committerhyokeun <hyokeun.jeon@samsung.com>
Fri, 21 Dec 2018 05:52:07 +0000 (14:52 +0900)
42 files changed:
.circleci/config.yml [new file with mode: 0644]
CHANGELOG.md
CONTRIBUTING.md
README.md
circle.yml [deleted file]
commands/command_lock.go
commands/command_ls_files.go
commands/command_migrate_import.go
commands/command_track.go
commands/command_uninstall.go
commands/command_untrack.go
commands/path.go
commands/run.go
config/version.go
debian/changelog
git-lfs.go
git/git.go
git/githistory/rewriter.go
lfs/attribute.go
lfsapi/auth.go
lfsapi/client.go
rpm/SPECS/git-lfs.spec
script/packagecloud.rb
test/cmd/lfstest-gitserver.go
test/test-credentials.sh
test/test-env.sh
test/test-extra-header.sh
test/test-install.sh
test/test-lock.sh
test/test-ls-files.sh
test/test-mergetool.sh [new file with mode: 0755]
test/test-migrate-fixtures.sh
test/test-migrate-import.sh
test/test-push.sh
test/test-track.sh
test/test-uninstall.sh
test/test-untrack.sh
test/test-version.sh [new file with mode: 0755]
test/test-worktree.sh
test/testhelpers.sh
tq/meter.go
versioninfo.json

diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644 (file)
index 0000000..c0ac283
--- /dev/null
@@ -0,0 +1,97 @@
+version: 2
+
+jobs:
+  bootstrap:
+    macos:
+      xcode: "9.2.0"
+    working_directory: ~/go/src/github.com/git-lfs/git-lfs
+    steps:
+      - checkout
+      - run: brew update
+      - run: brew prune
+      - run: brew upgrade go || brew install go
+      - run: brew upgrade git || brew install git
+      - run: brew upgrade gettext || brew install gettext
+      - run:
+          command: git clone https://github.com/git/git.git git-source
+          working_directory: ~/go/src/github.com/git-lfs/git-lfs
+      - run: echo "export GOPATH=$HOME/go" >> $BASH_ENV
+      - run: go get -d -v github.com/spf13/cobra
+      - run: go get -d -v github.com/ThomsonReutersEikon/go-ntlm/ntlm
+      - save_cache:
+          key: git-lfs-{{ .Branch }}-{{ .Revision }}
+          paths:
+            - /usr/local/Cellar
+            - ~/go
+
+  build_with_system_git:
+    macos:
+      xcode: "9.2.0"
+    working_directory: ~/go/src/github.com/git-lfs/git-lfs
+    environment:
+      GIT_SOURCE_REPO: https://github.com/git/git.git
+    steps:
+      - restore_cache:
+          key: git-lfs-{{ .Branch }}-{{ .Revision }}
+      - run: echo go gettext | xargs brew link --force
+      - run: echo "export GOPATH=$HOME/go" >> $BASH_ENV
+      - run: echo "export PATH=$HOME/bin:$GOPATH/bin:$PATH" >> $BASH_ENV
+      - run: echo "export GIT_LFS_TEST_DIR=$HOME/git-lfs-tests" >> $BASH_ENV
+      - run: script/cibuild
+
+  build_with_earliest_supported_git:
+    macos:
+      xcode: "9.2.0"
+    working_directory: ~/go/src/github.com/git-lfs/git-lfs
+    environment:
+      GIT_EARLIEST_SUPPORTED_VERSION: v2.0.0
+      GIT_SOURCE_REPO: https://github.com/git/git.git
+    steps:
+      - restore_cache:
+          key: git-lfs-{{ .Branch }}-{{ .Revision }}
+      - run: echo go gettext | xargs brew link --force
+      - run:
+          command: ./script/install-git-source "$GIT_EARLIEST_SUPPORTED_VERSION"
+          environment:
+            NO_OPENSSL: YesPlease
+            APPLE_COMMON_CRYPTO: YesPlease
+      - run: echo "export GOPATH=$HOME/go" >> $BASH_ENV
+      - run: echo "export PATH=$HOME/bin:$GOPATH/bin:$PATH" >> $BASH_ENV
+      - run: echo "export GIT_LFS_TEST_DIR=$HOME/git-lfs-tests" >> $BASH_ENV
+      - run: script/cibuild
+
+  build_with_latest_git:
+    macos:
+      xcode: "9.2.0"
+    working_directory: ~/go/src/github.com/git-lfs/git-lfs
+    environment:
+      GIT_LATEST_SOURCE_BRANCH: master
+      GIT_SOURCE_REPO: https://github.com/git/git.git
+    steps:
+      - restore_cache:
+          key: git-lfs-{{ .Branch }}-{{ .Revision }}
+      - run: echo go gettext | xargs brew link --force
+      - run:
+          command: ./script/install-git-source "$GIT_LATEST_SOURCE_BRANCH"
+          environment:
+            NO_OPENSSL: YesPlease
+            APPLE_COMMON_CRYPTO: YesPlease
+      - run: echo "export GOPATH=$HOME/go" >> $BASH_ENV
+      - run: echo "export PATH=$HOME/bin:$GOPATH/bin:$PATH" >> $BASH_ENV
+      - run: echo "export GIT_LFS_TEST_DIR=$HOME/git-lfs-tests" >> $BASH_ENV
+      - run: script/cibuild
+
+workflows:
+  version: 2
+  build:
+    jobs:
+      - bootstrap
+      - build_with_earliest_supported_git:
+          requires:
+            - bootstrap
+      - build_with_latest_git:
+          requires:
+            - bootstrap
+      - build_with_system_git:
+          requires:
+            - bootstrap
index 3854008..902b598 100644 (file)
@@ -1,5 +1,56 @@
 # Git LFS Changelog
 
+### 2.4.1 (18 May, 2018)
+
+This release fixes a handful of bugs found and fixed since v2.4.0. In
+particular, Git LFS no longer panic()'s after invalid API responses, can
+correctly run 'fetch' on SHAs instead of references, migrates symbolic links
+correctly, and avoids writing to `$HOME/.gitconfig` more than is necessary.
+
+We would like to extend a "thank you" to the following contributors for their
+gracious patches:
+
+- @QuLogic fixed an issue with running tests that require credentials
+- @patrickmarlier made it possible for 'git lfs migrate import' to work
+  correctly with symbolic links.
+- @zackse fixed an inconsistency in `CONTRIBUTING.md`
+- @zanglang fixed an inconsistency in `README.md`
+
+Git LFS would not be possible without generous contributions from the
+open-source community. For these, and many more: thank you!
+
+### Features
+
+* script/packagecloud.rb: release on Ubuntu Bionic #2961 (@ttaylorr)
+
+### Bugs
+
+* lfsapi: canonicalize extra HTTP headers #3010 (@ttaylorr)
+* commands/lock: follow symlinks before locking #2996 (@ttaylorr)
+* lfs/attribute.go: remove default value from upgradeables #2994 (@ttaylorr)
+* git: include SHA1 in ref-less revisions #2982 (@ttaylorr)
+* Do not migrate the symlinks to LFS objects. #2983 (@patrickmarlier)
+* commands/uninstall: do not log about global hooks with --local #2976 (@ttaylorr)
+* commands/run.go: exit 127 on unknown sub-command #2969 (@ttaylorr)
+* commands/{un,}track: perform "prefix-agnostic" comparisons #2955 (@ttaylorr)
+* commands/migrate: escape paths before .gitattributes  #2933 (@ttaylorr)
+* commands/ls-files: do not accept '--all' after '--' #2932 (@ttaylorr)
+* tq: prevent uint64 underflow with invalid API response #2902 (@ttaylorr)
+
+### Misc
+
+* test/test-env: skip comparing GIT_EXEC_PATH #3015 (@ttaylorr)
+* remove reference to CLA from contributor's guide #2997 (@zackse)
+* .gitattributes link is broken #2985 (@zanglang)
+* commands: make --version a synonym for 'version' #2968, #3017 (@ttaylorr)
+* test: ensure that git-mergetool(1) works with large files #2939 (@ttaylorr)
+* README.md: note the correct PackageCloud URL #2960 (@ttaylorr)
+* README.md: mention note about `git lfs track` retroactively #2948 (@ttaylorr)
+* README.md: reorganize into Core Team, Alumni #2941 (@ttaylorr)
+* README.md: :nail_care: #2942 (@ttaylorr)
+* circle.yml: upgrade to 'version: 2' syntax #2928 (@ttaylorr)
+* Use unique repo name for tests that require credentials. #2901 (@QuLogic)
+
 ## 2.4.0 (2 March, 2018)
 
 This release introduces a rewrite of the underlying file matching engine,
index dd24e8b..166d091 100644 (file)
@@ -63,7 +63,6 @@ In general, contributors should develop on branches based off of `master` and pu
 0. Create a new branch based on `master`: `git checkout -b <my-branch-name> master`
 0. Make your change, add tests, and make sure the tests still pass
 0. Push to your fork and [submit a pull request][pr] from your branch to `master`
-0. Accept the [GitHub CLA][cla]
 0. Pat yourself on the back and wait for your pull request to be reviewed
 
 Here are a few things you can do that will increase the likelihood of your pull request being accepted:
@@ -161,4 +160,3 @@ v1.5 just shipped, set the version in master to `1.6-pre`, for example.
 [fork]: https://github.com/git-lfs/git-lfs/fork
 [pr]: https://github.com/git-lfs/git-lfs/compare
 [style]: https://github.com/golang/go/wiki/CodeReviewComments
-[cla]: https://cla.github.com/git-lfs/git-lfs/accept
index 1932fff..8bc3c65 100644 (file)
--- a/README.md
+++ b/README.md
 [5]: https://ci.appveyor.com/api/projects/status/46a5yoqc3hk59bl5/branch/master?svg=true
 [6]: https://ci.appveyor.com/project/git-lfs/git-lfs/branch/master
 
-Git LFS is a command line extension and [specification](docs/spec.md) for
-managing large files with Git. The client is written in Go, with pre-compiled
-binaries available for Mac, Windows, Linux, and FreeBSD. Check out the
-[Git LFS website][page] for an overview of features.
+[Git LFS](https://git-lfs.github.com) is a command line extension and
+[specification](docs/spec.md) for managing large files with Git.
 
-[page]: https://git-lfs.github.com/
+The client is written in Go, with pre-compiled binaries available for Mac,
+Windows, Linux, and FreeBSD. Check out the [website](http://git-lfs.github.com)
+for an overview of features.
 
 ## Getting Started
 
-By default, the Git LFS client needs a Git LFS server to sync the large files
-it manages. This works out of the box when using popular git repository
-hosting providers like GitHub, Atlassian, etc. When you host your own
-vanilla git server, for example, you need to either use a separate
-[Git LFS server instance](https://github.com/git-lfs/git-lfs/wiki/Implementations),
-or use the [custom transfer adapter](docs/custom-transfers.md) with
-a transfer agent in blind mode, without having to use a Git LFS server instance.
+### Installation
 
-You can install the Git LFS client in several different ways, depending on
-your setup and preferences.
+You can install the Git LFS client in several different ways, depending on your
+setup and preferences.
 
-* Linux users can install Debian or RPM packages from [PackageCloud](https://packagecloud.io/github/git-lfs/install).  See the [Installation Guide](./INSTALLING.md) for details.
-* Mac users can install from [Homebrew](https://github.com/Homebrew/homebrew) with `brew install git-lfs`, or from [MacPorts](https://www.macports.org) with `port install git-lfs`.
-* Windows users can install from [Chocolatey](https://chocolatey.org/) with `choco install git-lfs`.
-* [Binary packages are available][rel] for Windows, Mac, Linux, and FreeBSD.
-* You can build it with Go 1.8.1+. See the [Contributing Guide](./CONTRIBUTING.md) for instructions.
+* **Linux users**. Debian and RPM packages are available from
+  [PackageCloud](https://packagecloud.io/github/git-lfs/install).
+* **macOS users**. [Homebrew](https://brew.sh) bottles are distributed, and can
+  be installed via `brew install git-lfs`.
+* **Windows users**. Chocolatey packages are distributed, and can be installed
+  via `choco install git-lfs`.
 
-[rel]: https://github.com/git-lfs/git-lfs/releases
+In addition, [binary packages](https://github.com/git-lfs/git-lfs/releases) are
+available for Linux, macOS, Windows, and FreeBSD. This repository can also be
+built-from-source using the latest version of [Go](https://golang.org).
 
-Note: Git LFS requires Git v1.8.5 or higher.
+### Usage
 
-Once installed, you need to setup the global Git hooks for Git LFS. This only
-needs to be done once per machine.
+Git LFS requires a global installation once per-machine. This can be done by
+running:
 
 ```bash
 $ git lfs install
 ```
 
-Now, it's time to add some large files to a repository. The first step is to
-specify file patterns to store with Git LFS. These file patterns are stored in
-`.gitattributes`.
+To begin using Git LFS within your Git repository, you can indicate which files
+you would like Git LFS to manage. This can be done by running the following
+_from within Git repository_:
 
 ```bash
-$ mkdir large-repo
-$ cd large-repo
-$ git init
-
-# Add all zip files through Git LFS
-$ git lfs track "*.zip"
+$ git lfs track "*.psd"
 ```
 
-Now you're ready to push some commits:
+(Where `*.psd` is the pattern of filenames that you wish to track. You can read
+more about this pattern syntax
+[here](https://git-scm.com/docs/gitattributes)).
+
+After any invocation of `git-lfs-track(1)` or `git-lfs-untrack(1)`, you _must
+commit changes to your `.gitattributes` file_. This can be done by running:
 
 ```bash
 $ git add .gitattributes
-$ git add my.zip
-$ git commit -m "add zip"
+$ git commit -m "track *.psd files using Git LFS"
 ```
 
-You can confirm that Git LFS is managing your zip file:
+You can now interact with your Git repository as usual, and Git LFS will take
+care of managing your large files. For example, changing a file named `my.psd`
+(tracked above via `*.psd`):
+
+```bash
+$ git add my.psd
+$ git commit -m "add psd"
+```
+
+> _Tip:_ if you have large files already in your repository's history, `git lfs
+> track` will _not_ track them retroactively. To migrate existing large files
+> in your history to use Git LFS, use `git lfs migrate`. For example:
+>
+> ```
+> $ git lfs migrate import --include="*.psd"
+> ```
+>
+> For more information, read [`git-lfs-migrate(1)`](https://github.com/git-lfs/git-lfs/blob/master/docs/man/git-lfs-migrate.1.ronn).
+
+You can confirm that Git LFS is managing your PSD file:
 
 ```bash
 $ git lfs ls-files
-my.zip
+3c2f7aedfb * my.psd
 ```
 
 Once you've made your commits, push your files to the Git remote:
 
 ```bash
 $ git push origin master
-Sending my.zip
-LFS: 12.58 MB / 12.58 MB  100.00 %
-Counting objects: 2, done.
-Delta compression using up to 8 threads.
-Compressing objects: 100% (5/5), done.
-Writing objects: 100% (5/5), 548 bytes | 0 bytes/s, done.
-Total 5 (delta 1), reused 0 (delta 0)
+Uploading LFS objects: 100% (1/1), 810 B, 1.2 KB/s
+# ...
 To https://github.com/git-lfs/git-lfs-test
    67fcf6a..47b2002  master -> master
 ```
 
+Note: Git LFS requires Git v1.8.5 or higher.
+
 ## Limitations
 
 Git LFS maintains a list of currently known limitations, which you can find and
@@ -105,41 +117,54 @@ $ git lfs help <subcommand>
 ```
 
 The [official documentation](docs) has command references and specifications for
-the tool. You can ask questions in the [Git LFS chat room][chat], or [file a new
-issue][ish]. Be sure to include details about the problem so we can
-troubleshoot it.
+the tool.
 
-1. Include the output of `git lfs env`, which shows how your Git environment
-is setup.
-2. Include `GIT_TRACE=1` in any bad Git commands to enable debug messages.
-3. If the output includes a message like `Errors logged to /path/to/.git/lfs/objects/logs/*.log`,
-throw the contents in the issue, or as a link to a Gist or paste site.
+You can always [open an issue](https://github.com/git-lfs/git-lfs/issues), and
+one of the Core Team members will respond to you. Please be sure to include:
 
-[chat]: https://gitter.im/git-lfs/git-lfs
-[ish]: https://github.com/git-lfs/git-lfs/issues
+1. The output of `git lfs env`, which displays helpful information about your
+   Git repository useful in debugging.
+2. Any failed commands re-run with `GIT_TRACE=1` in the environment, which
+   displays additional information pertaining to why a command crashed.
 
 ## Contributing
 
 See [CONTRIBUTING.md](CONTRIBUTING.md) for info on working on Git LFS and
 sending patches. Related projects are listed on the [Implementations wiki
-page][impl]. You can also join [the project's chat room][chat].
+page](https://github.com/git-lfs/git-lfs/wiki/Implementations).
+
+## Core Team
+
+These are the humans that form the Git LFS core team, which runs the project.
 
-[impl]: https://github.com/git-lfs/git-lfs/wiki/Implementations
+In alphabetical order:
 
-### Using LFS from other Go code
+| [@larsxschneider][larsxschneider-user] | [@ttaylorr][ttaylorr-user] |
+|---|---|
+| [![][larsxschneider-img]][larsxschneider-user] | [![][ttaylorr-img]][ttaylorr-user] |
 
-At the moment git-lfs is only focussed on the stability of its command line
-interface, and the [server APIs](docs/api/README.md). The contents of the
-source packages is subject to change. We therefore currently discourage other
-Go code from depending on the git-lfs packages directly; an API to be used by
-external Go code may be provided in future.
+[larsxschneider-img]: https://avatars1.githubusercontent.com/u/477434?s=100&v=4
+[ttaylorr-img]: https://avatars2.githubusercontent.com/u/443245?s=100&v=4
+[larsxschneider-user]: https://github.com/larsxschneider
+[ttaylorr-user]: https://github.com/ttaylorr
 
-## Core Team
+### Alumni
 
-These are the humans that form the Git LFS core team, which runs the project.
+These are the humans that have in the past formed the Git LFS core team, or
+have otherwise contributed a significant amount to the project. Git LFS would
+not be possible without them.
 
 In alphabetical order:
 
-| [@andyneff](https://github.com/andyneff) | [@rubyist](https://github.com/rubyist) | [@sinbad](https://github.com/sinbad) | [@technoweenie](https://github.com/technoweenie) | [@ttaylorr](https://github.com/ttaylorr) |
-|---|---|---|---|---|
-| [![](https://avatars1.githubusercontent.com/u/7596961?v=3&s=100)](https://github.com/andyneff) | [![](https://avatars1.githubusercontent.com/u/143?v=3&s=100)](https://github.com/rubyist) | [![](https://avatars1.githubusercontent.com/u/142735?v=3&s=100)](https://github.com/sinbad) | [![](https://avatars3.githubusercontent.com/u/21?v=3&s=100)](https://github.com/technoweenie) | [![](https://avatars3.githubusercontent.com/u/443245?v=3&s=100)](https://github.com/ttaylorr) |
+| [@andyneff][andyneff-user] | [@rubyist][rubyist-user] | [@sinbad][sinbad-user] | [@technoweenie][technoweenie-user] |
+|---|---|---|---|
+| [![][andyneff-img]][andyneff-user] | [![][rubyist-img]][rubyist-user] | [![][sinbad-img]][sinbad-user] | [![][technoweenie-img]][technoweenie-user] |
+
+[andyneff-img]: https://avatars1.githubusercontent.com/u/7596961?v=3&s=100
+[rubyist-img]: https://avatars1.githubusercontent.com/u/143?v=3&s=100
+[sinbad-img]: https://avatars1.githubusercontent.com/u/142735?v=3&s=100
+[technoweenie-img]: https://avatars3.githubusercontent.com/u/21?v=3&s=100
+[andyneff-user]: https://github.com/andyneff
+[sinbad-user]: https://github.com/sinbad
+[rubyist-user]: https://github.com/rubyist
+[technoweenie-user]: https://github.com/technoweenie
diff --git a/circle.yml b/circle.yml
deleted file mode 100644 (file)
index 4f79fc5..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-machine:
-  environment:
-    GIT_LFS_TEST_DIR: $HOME/git-lfs-tests
-    GIT_SOURCE_REPO: https://github.com/git/git.git
-    GIT_EARLIEST_SUPPORTED_VERSION: v2.0.0
-    GIT_LATEST_SOURCE_BRANCH: master
-    XCODE_SCHEME: test
-    XCODE_WORKSPACE: test
-    XCODE_PROJECT: test
-    GOPATH: $HOME/git-lfs/.go
-    NO_OPENSSL: YesPlease
-    APPLE_COMMON_CRYPTO: YesPlease
-  xcode:
-    version: 8.2
-
-general:
-  build_dir: .go/src/github.com/git-lfs/git-lfs
-
-checkout:
-  post:
-    - mkdir -p ~/git-lfs/.go/src/github.com/git-lfs
-    - ln -s ~/git-lfs ~/git-lfs/.go/src/github.com/git-lfs
-
-dependencies:
-  pre:
-    - brew update
-    - brew prune
-    - brew upgrade go || brew install go
-    - brew upgrade git || brew install git
-    - brew upgrade gettext || brew install gettext
-    - brew link --force gettext
-    - git clone $GIT_SOURCE_REPO git-source
-  override:
-    - script/bootstrap
-
-    # needed for git-lfs-test-server-api
-    - go get -d -v github.com/spf13/cobra
-    - go get -d -v github.com/ThomsonReutersEikon/go-ntlm/ntlm
-
-test:
-  override:
-    - script/cibuild
-    - script/install-git-source "$GIT_EARLIEST_SUPPORTED_VERSION"
-    - PATH="$HOME/bin:$PATH" SKIPCOMPILE=1 script/integration
-    - script/install-git-source "$GIT_LATEST_SOURCE_BRANCH"
-    - PATH="$HOME/bin:$PATH" SKIPCOMPILE=1 script/integration
index eb06104..ab88d7d 100644 (file)
@@ -79,6 +79,11 @@ func lockPath(file string) (string, error) {
        if err != nil {
                return "", err
        }
+       wd, err = filepath.EvalSymlinks(wd)
+       if err != nil {
+               return "", errors.Wrapf(err,
+                       "could not follow symlinks for %s", wd)
+       }
 
        abs := filepath.Join(wd, file)
        path := strings.TrimPrefix(abs, repo)
index f692d29..612ce58 100644 (file)
@@ -45,17 +45,17 @@ func lsFilesCommand(cmd *cobra.Command, args []string) {
        seen := make(map[string]struct{})
 
        gitscanner := lfs.NewGitScanner(func(p *lfs.WrappedPointer, err error) {
+               if err != nil {
+                       Exit("Could not scan for Git LFS tree: %s", err)
+                       return
+               }
+
                if !lsFilesScanAll {
                        if _, ok := seen[p.Name]; ok {
                                return
                        }
                }
 
-               if err != nil {
-                       Exit("Could not scan for Git LFS tree: %s", err)
-                       return
-               }
-
                if debug {
                        Print(
                                "filepath: %s\n"+
index b9ac784..606ba4c 100644 (file)
@@ -123,11 +123,11 @@ func trackedFromFilter(filter *filepathfilter.Filter) *tools.OrderedSet {
        tracked := tools.NewOrderedSet()
 
        for _, include := range filter.Include() {
-               tracked.Add(fmt.Sprintf("%s filter=lfs diff=lfs merge=lfs -text", include))
+               tracked.Add(fmt.Sprintf("%s filter=lfs diff=lfs merge=lfs -text", escapeAttrPattern(include)))
        }
 
        for _, exclude := range filter.Exclude() {
-               tracked.Add(fmt.Sprintf("%s text -filter -merge -diff", exclude))
+               tracked.Add(fmt.Sprintf("%s text -filter -merge -diff", escapeAttrPattern(exclude)))
        }
 
        return tracked
index 3472f9c..5ba0692 100644 (file)
@@ -67,7 +67,7 @@ func trackCommand(cmd *cobra.Command, args []string) {
        var writeablePatterns []string
 ArgsLoop:
        for _, unsanitizedPattern := range args {
-               pattern := cleanRootPath(unsanitizedPattern)
+               pattern := trimCurrentPrefix(cleanRootPath(unsanitizedPattern))
                if !trackNoModifyAttrsFlag {
                        for _, known := range knownPatterns {
                                if known.Path == filepath.Join(relpath, pattern) &&
@@ -81,7 +81,7 @@ ArgsLoop:
                }
 
                // Generate the new / changed attrib line for merging
-               encodedArg := escapeTrackPattern(pattern)
+               encodedArg := escapeAttrPattern(pattern)
                lockableArg := ""
                if trackLockableFlag { // no need to test trackNotLockableFlag, if we got here we're disabling
                        lockableArg = " " + git.LockableAttrib
@@ -95,7 +95,7 @@ ArgsLoop:
                        writeablePatterns = append(writeablePatterns, pattern)
                }
 
-               Print("Tracking %q", unescapeTrackPattern(encodedArg))
+               Print("Tracking %q", unescapeAttrPattern(encodedArg))
        }
 
        // Now read the whole local attributes file and iterate over the contents,
@@ -258,7 +258,7 @@ var (
        }
 )
 
-func escapeTrackPattern(unescaped string) string {
+func escapeAttrPattern(unescaped string) string {
        var escaped string = strings.Replace(unescaped, `\`, "/", -1)
 
        for from, to := range trackEscapePatterns {
@@ -268,7 +268,7 @@ func escapeTrackPattern(unescaped string) string {
        return escaped
 }
 
-func unescapeTrackPattern(escaped string) string {
+func unescapeAttrPattern(escaped string) string {
        var unescaped string = escaped
 
        for to, from := range trackEscapePatterns {
index b27336c..bdea826 100644 (file)
@@ -14,7 +14,9 @@ func uninstallCommand(cmd *cobra.Command, args []string) {
                uninstallHooksCommand(cmd, args)
        }
 
-       Print("Global Git LFS configuration has been removed.")
+       if !localInstall {
+               Print("Global Git LFS configuration has been removed.")
+       }
 }
 
 // uninstallHooksCmd removes any hooks created by Git LFS.
index 0b51fca..f15bfbb 100644 (file)
@@ -55,7 +55,7 @@ func untrackCommand(cmd *cobra.Command, args []string) {
 
                path := strings.Fields(line)[0]
                if removePath(path, args) {
-                       Print("Untracking %q", unescapeTrackPattern(path))
+                       Print("Untracking %q", unescapeAttrPattern(path))
                } else {
                        attributesFile.WriteString(line + "\n")
                }
@@ -63,8 +63,9 @@ func untrackCommand(cmd *cobra.Command, args []string) {
 }
 
 func removePath(path string, args []string) bool {
+       withoutCurrentDir := trimCurrentPrefix(path)
        for _, t := range args {
-               if path == escapeTrackPattern(t) {
+               if withoutCurrentDir == escapeAttrPattern(trimCurrentPrefix(t)) {
                        return true
                }
        }
index 1aa7083..4e7568c 100644 (file)
@@ -12,6 +12,24 @@ func gitLineEnding(git env) string {
        }
 }
 
+const (
+       windowsPrefix = `.\`
+       nixPrefix     = `./`
+)
+
+// trimCurrentPrefix removes a leading prefix of "./" or ".\" (referring to the
+// current directory in a platform independent manner).
+//
+// It is useful for callers such as "git lfs track" and "git lfs untrack", that
+// wish to compare filepaths and/or attributes patterns without cleaning across
+// multiple platforms.
+func trimCurrentPrefix(p string) string {
+       if strings.HasPrefix(p, windowsPrefix) {
+               return strings.TrimPrefix(p, windowsPrefix)
+       }
+       return strings.TrimPrefix(p, nixPrefix)
+}
+
 type env interface {
        Get(string) (string, bool)
 }
index 6cc5fae..823e76b 100644 (file)
@@ -16,6 +16,8 @@ import (
 var (
        commandFuncs []func() *cobra.Command
        commandMu    sync.Mutex
+
+       rootVersion bool
 )
 
 // NewCommand creates a new 'git-lfs' sub command, given a command name and
@@ -49,7 +51,9 @@ func RegisterCommand(name string, runFn func(cmd *cobra.Command, args []string),
 
 // Run initializes the 'git-lfs' command and runs it with the given stdin and
 // command line args.
-func Run() {
+//
+// It returns an exit code.
+func Run() int {
        log.SetOutput(ErrorWriter)
 
        root := NewCommand("git-lfs", gitlfsCommand)
@@ -60,6 +64,8 @@ func Run() {
        root.SetHelpFunc(helpCommand)
        root.SetUsageFunc(usageCommand)
 
+       root.Flags().BoolVarP(&rootVersion, "version", "v", false, "")
+
        cfg = config.New()
 
        for _, f := range commandFuncs {
@@ -68,13 +74,20 @@ func Run() {
                }
        }
 
-       root.Execute()
+       err := root.Execute()
        closeAPIClient()
+
+       if err != nil {
+               return 127
+       }
+       return 0
 }
 
 func gitlfsCommand(cmd *cobra.Command, args []string) {
        versionCommand(cmd, args)
-       cmd.Usage()
+       if !rootVersion {
+               cmd.Usage()
+       }
 }
 
 func helpCommand(cmd *cobra.Command, args []string) {
index d03a139..634fc2b 100644 (file)
@@ -12,7 +12,7 @@ var (
 )
 
 const (
-       Version = "2.4.0"
+       Version = "2.4.1"
 )
 
 func init() {
index 6dbd5b2..367060a 100644 (file)
@@ -1,3 +1,9 @@
+git-lfs (2.4.1) stable; urgency=low
+
+  * New upstream version
+
+ -- Taylor Blau <me@ttaylorr.com>  Mon, 21 May 2018 14:29:00 +0000
+
 git-lfs (2.4.0) stable; urgency=low
 
   * New upstream version
index c185eea..d8d66b4 100644 (file)
@@ -32,6 +32,7 @@ func main() {
                }
        }()
 
-       commands.Run()
+       code := commands.Run()
        once.Do(commands.Cleanup)
+       os.Exit(code)
 }
index bd5b931..fd54257 100644 (file)
@@ -245,7 +245,8 @@ func ResolveRef(ref string) (*Ref, error) {
 
        if len(lines) == 1 {
                // ref is a sha1 and has no symbolic-full-name
-               fullref.Name = lines[0] // fullref.Sha
+               fullref.Name = lines[0]
+               fullref.Sha = lines[0]
                fullref.Type = RefTypeOther
                return fullref, nil
        }
index 566e57e..b83cb2b 100644 (file)
@@ -322,6 +322,12 @@ func (r *Rewriter) rewriteTree(commitOID []byte, treeOID []byte, path string, fn
                        continue
                }
 
+               // If this is a symlink, skip it
+               if entry.Filemode == 0120000 {
+                       entries = append(entries, copyEntry(entry))
+                       continue
+               }
+
                if cached := r.uncacheEntry(entry); cached != nil {
                        entries = append(entries, copyEntry(cached))
                        continue
index fee511a..bbe6f52 100644 (file)
@@ -56,7 +56,21 @@ func filterAttribute() *Attribute {
                        "process":  "git-lfs filter-process",
                        "required": "true",
                },
-               Upgradeables: upgradeables(),
+               Upgradeables: map[string][]string{
+                       "clean": []string{
+                               "git-lfs clean %f",
+                       },
+                       "smudge": []string{
+                               "git-lfs smudge %f",
+                               "git-lfs smudge --skip %f",
+                               "git-lfs smudge --skip -- %f",
+                       },
+                       "process": []string{
+                               "git-lfs filter",
+                               "git-lfs filter --skip",
+                               "git-lfs filter-process --skip",
+                       },
+               },
        }
 }
 
@@ -69,24 +83,20 @@ func skipSmudgeFilterAttribute() *Attribute {
                        "process":  "git-lfs filter-process --skip",
                        "required": "true",
                },
-               Upgradeables: upgradeables(),
-       }
-}
-
-func upgradeables() map[string][]string {
-       return map[string][]string{
-               "clean": []string{"git-lfs clean %f"},
-               "smudge": []string{
-                       "git-lfs smudge %f",
-                       "git-lfs smudge --skip %f",
-                       "git-lfs smudge -- %f",
-                       "git-lfs smudge --skip -- %f",
-               },
-               "process": []string{
-                       "git-lfs filter",
-                       "git-lfs filter --skip",
-                       "git-lfs filter-process",
-                       "git-lfs filter-process --skip",
+               Upgradeables: map[string][]string{
+                       "clean": []string{
+                               "git-lfs clean -- %f",
+                       },
+                       "smudge": []string{
+                               "git-lfs smudge %f",
+                               "git-lfs smudge --skip %f",
+                               "git-lfs smudge -- %f",
+                       },
+                       "process": []string{
+                               "git-lfs filter",
+                               "git-lfs filter --skip",
+                               "git-lfs filter-process",
+                       },
                },
        }
 }
index d4bcc4a..837e217 100644 (file)
@@ -270,6 +270,8 @@ func hasScheme(what string) bool {
 }
 
 func requestHasAuth(req *http.Request) bool {
+       // The "Authorization" string constant is safe, since we assume that all
+       // request headers have been canonicalized.
        if len(req.Header.Get("Authorization")) > 0 {
                return true
        }
index ce20c3a..dd6c652 100644 (file)
@@ -7,6 +7,7 @@ import (
        "io"
        "net"
        "net/http"
+       "net/textproto"
        "net/url"
        "os"
        "regexp"
@@ -136,6 +137,13 @@ func (c *Client) extraHeaders(u *url.URL) map[string][]string {
                }
 
                k, v := parts[0], strings.TrimSpace(parts[1])
+               // If header keys are given in non-canonicalized form (e.g.,
+               // "AUTHORIZATION" as opposed to "Authorization") they will not
+               // be returned in calls to net/http.Header.Get().
+               //
+               // So, we avoid this problem by first canonicalizing header keys
+               // for extra headers.
+               k = textproto.CanonicalMIMEHeaderKey(k)
 
                m[k] = append(m[k], v)
        }
index f4f4060..74343b0 100644 (file)
@@ -1,5 +1,5 @@
 Name:           git-lfs
-Version:        2.4.0
+Version:        2.4.1
 Release:        1%{?dist}
 Summary:        Git extension for versioning large files
 
index 6a57347..1b8b138 100644 (file)
@@ -62,6 +62,7 @@ $distro_name_map = {
     ubuntu/yakkety
     ubuntu/zesty
     ubuntu/artful
+    ubuntu/bionic
   ),
 }
 
index 87a1d4e..9773299 100644 (file)
@@ -1246,7 +1246,7 @@ func locksHandler(w http.ResponseWriter, r *http.Request, repo string) {
 }
 
 func missingRequiredCreds(w http.ResponseWriter, r *http.Request, repo string) bool {
-       if repo != "requirecreds" {
+       if !strings.HasPrefix(repo, "requirecreds") {
                return false
        }
 
index 0ebf93a..3efbe6b 100755 (executable)
@@ -294,9 +294,9 @@ begin_test "credentials from lfs.url"
 (
   set -e
 
-  reponame="requirecreds"
+  reponame="requirecreds-lfsurl"
   setup_remote_repo "$reponame"
-  clone_repo "$reponame" requirecreds-lfsurl
+  clone_repo "$reponame" "$reponame"
 
   git lfs track "*.dat"
   echo "push a" > a.dat
@@ -335,9 +335,9 @@ begin_test "credentials from remote.origin.url"
 (
   set -e
 
-  reponame="requirecreds"
+  reponame="requirecreds-remoteurl"
   setup_remote_repo "$reponame"
-  clone_repo "$reponame" requirecreds-remoteurl
+  clone_repo "$reponame" "$reponame"
 
   git lfs track "*.dat"
   echo "push b" > b.dat
index 5879bff..2a3047e 100755 (executable)
@@ -50,7 +50,7 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
 
   contains_same_elements "$expected" "$actual"
 )
@@ -102,12 +102,12 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$endpoint" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual"
 
   cd .git
   expected2=$(echo "$expected" | sed -e 's/LocalWorkingDir=.*/LocalWorkingDir=/')
-  actual2=$(git lfs env)
+  actual2=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected2" "$actual2"
 )
 end_test
@@ -161,12 +161,12 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$endpoint" "$endpoint2" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual"
 
   cd .git
   expected2=$(echo "$expected" | sed -e 's/LocalWorkingDir=.*/LocalWorkingDir=/')
-  actual2=$(git lfs env)
+  actual2=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected2" "$actual2"
 )
 end_test
@@ -218,12 +218,12 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$endpoint" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual"
 
   cd .git
   expected2=$(echo "$expected" | sed -e 's/LocalWorkingDir=.*/LocalWorkingDir=/')
-  actual2=$(git lfs env)
+  actual2=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected2" "$actual2"
 )
 end_test
@@ -277,12 +277,12 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$endpoint" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual"
 
   cd .git
   expected2=$(echo "$expected" | sed -e 's/LocalWorkingDir=.*/LocalWorkingDir=/')
-  actual2=$(git lfs env)
+  actual2=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected2" "$actual2"
 )
 end_test
@@ -337,12 +337,12 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual"
 
   cd .git
   expected2=$(echo "$expected" | sed -e 's/LocalWorkingDir=.*/LocalWorkingDir=/')
-  actual2=$(git lfs env)
+  actual2=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected2" "$actual2"
 )
 end_test
@@ -398,12 +398,12 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual"
 
   cd .git
   expected2=$(echo "$expected" | sed -e 's/LocalWorkingDir=.*/LocalWorkingDir=/')
-  actual2=$(git lfs env)
+  actual2=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected2" "$actual2"
 )
 end_test
@@ -466,12 +466,12 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual"
 
   mkdir a
   cd a
-  actual2=$(git lfs env)
+  actual2=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual2"
 )
 end_test
@@ -522,19 +522,23 @@ UploadTransfers=basic
 %s
 ' "$(git lfs version)" "$(git version)" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
 
-  actual=$(GIT_DIR=$gitDir GIT_WORK_TREE=$workTree git lfs env)
+  actual=$(GIT_DIR=$gitDir GIT_WORK_TREE=$workTree git lfs env \
+            | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual"
 
   cd $TRASHDIR/$reponame
-  actual2=$(GIT_DIR=$gitDir GIT_WORK_TREE=$workTree git lfs env)
+  actual2=$(GIT_DIR=$gitDir GIT_WORK_TREE=$workTree git lfs env \
+            | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual2"
 
   cd $TRASHDIR/$reponame/.git
-  actual3=$(GIT_DIR=$gitDir GIT_WORK_TREE=$workTree git lfs env)
+  actual3=$(GIT_DIR=$gitDir GIT_WORK_TREE=$workTree git lfs env \
+            | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual3"
 
   cd $TRASHDIR/$reponame/a/b/c
-  actual4=$(GIT_DIR=$gitDir GIT_WORK_TREE=$workTree git lfs env)
+  actual4=$(GIT_DIR=$gitDir GIT_WORK_TREE=$workTree git lfs env \
+            | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual4"
 
   envVars="$(GIT_DIR=$gitDir GIT_WORK_TREE=a/b env | grep "^GIT" | sort)"
@@ -565,7 +569,8 @@ DownloadTransfers=basic
 UploadTransfers=basic
 %s
 ' "$(git lfs version)" "$(git version)" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars")
-  actual5=$(GIT_DIR=$gitDir GIT_WORK_TREE=a/b git lfs env)
+  actual5=$(GIT_DIR=$gitDir GIT_WORK_TREE=a/b git lfs env \
+            | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected5" "$actual5"
 
   cd $TRASHDIR/$reponame/a/b
@@ -598,7 +603,7 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual7=$(GIT_DIR=$gitDir git lfs env)
+  actual7=$(GIT_DIR=$gitDir git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected7" "$actual7"
 
   cd $TRASHDIR/$reponame/a
@@ -631,7 +636,7 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual8=$(GIT_WORK_TREE=$workTree git lfs env)
+  actual8=$(GIT_WORK_TREE=$workTree git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected8" "$actual8"
 )
 end_test
@@ -676,7 +681,7 @@ UploadTransfers=basic
 %s
 %s
 " "$(git lfs version)" "$(git version)" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expected" "$actual"
 
 )
@@ -698,7 +703,8 @@ Endpoint (other)=https://other-git-server.com/user/repo.git/info/lfs (auth=none)
   SSH=git@other-git-server.com:user/repo.git
 GIT_SSH=lfs-ssh-echo'
 
-  contains_same_elements "$expected" "$(git lfs env | grep -e "Endpoint" -e "SSH=")"
+  contains_same_elements "$expected" "$(git lfs env \
+    | grep -v "^GIT_EXEC_PATH=" | grep -e "Endpoint" -e "SSH=")"
 )
 end_test
 
@@ -754,7 +760,7 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expectedenabled" "$actual"
 
   git config --unset lfs.skipdownloaderrors
@@ -787,11 +793,11 @@ UploadTransfers=basic
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expecteddisabled" "$actual"
 
   # now enable via env var
-  actual=$(GIT_LFS_SKIP_DOWNLOAD_ERRORS=1 git lfs env)
+  actual=$(GIT_LFS_SKIP_DOWNLOAD_ERRORS=1 git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expectedenabled" "$actual"
 
 
@@ -853,7 +859,7 @@ UploadTransfers=basic,supertransfer,tus
 %s
 %s
 ' "$(git lfs version)" "$(git version)" "$localwd" "$localgit" "$localgitstore" "$localmedia" "$tempdir" "$lfsstorage" "$envVars" "$envInitConfig")
-  actual=$(git lfs env)
+  actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
   contains_same_elements "$expectedenabled" "$actual"
 
 )
index 823a7d6..b3a5f91 100755 (executable)
@@ -30,11 +30,11 @@ begin_test "http.<url>.extraHeader with authorization"
 (
   set -e
 
-  reponame="requirecreds"
+  reponame="requirecreds-extraHeader"
   setup_remote_repo "$reponame"
   clone_repo "$reponame" "$reponame"
 
-  # See: test/cmd/lfstest-gitserver.go:1176.
+  # See: test/cmd/lfstest-gitserver.go:missingRequiredCreds().
   user="requirecreds"
   pass="pass"
   auth="Basic $(echo -n $user:$pass | base64)"
@@ -59,3 +59,40 @@ begin_test "http.<url>.extraHeader with authorization"
   [ "0" -eq "$(grep -c "creds: git credential reject" curl.log)" ]
 )
 end_test
+
+begin_test "http.<url>.extraHeader with authorization (casing)"
+(
+  set -e
+
+  reponame="requirecreds-extraHeaderCasing"
+  setup_remote_repo "$reponame"
+  clone_repo "$reponame" "$reponame"
+
+  # See: test/cmd/lfstest-gitserver.go:missingRequiredCreds().
+  user="requirecreds"
+  pass="pass"
+  auth="Basic $(echo -n $user:$pass | base64)"
+
+  git config --local --add lfs.access basic
+  # N.B.: "AUTHORIZATION" is not the correct casing, and is therefore the
+  # subject of this test. See lfsapi.Client.extraHeaders() for more.
+  git config --local --add "http.extraHeader" "AUTHORIZATION: $auth"
+
+  git lfs track "*.dat"
+  printf "contents" > a.dat
+  git add .gitattributes a.dat
+  git commit -m "initial commit"
+
+  git push origin master 2>&1 | tee curl.log
+  if [ "0" -ne "${PIPESTATUS[0]}" ]; then
+    echo >&2 "expected \`git push origin master\` to succeed, didn't"
+    exit 1
+  fi
+
+  [ "0" -eq "$(grep -c "creds: filling with GIT_ASKPASS" curl.log)" ]
+  [ "0" -eq "$(grep -c "creds: git credential approve" curl.log)" ]
+  [ "0" -eq "$(grep -c "creds: git credential cache" curl.log)" ]
+  [ "0" -eq "$(grep -c "creds: git credential fill" curl.log)" ]
+  [ "0" -eq "$(grep -c "creds: git credential reject" curl.log)" ]
+)
+end_test
index d4ef864..abc8f64 100755 (executable)
@@ -4,17 +4,22 @@
 
 begin_test "install again"
 (
-  set -e
+  set -eo pipefail
 
   smudge="$(git config filter.lfs.smudge)"
   clean="$(git config filter.lfs.clean)"
   filter="$(git config filter.lfs.process)"
 
-  printf "$smudge" | grep "git-lfs smudge"
-  printf "$clean" | grep "git-lfs clean"
-  printf "$filter" | grep "git-lfs filter-process"
+  [ "$smudge" = "git-lfs smudge -- %f" ]
+  [ "$clean" = "git-lfs clean -- %f" ]
+  [ "$filter" = "git-lfs filter-process" ]
 
-  git lfs install
+  GIT_TRACE=1 git lfs install --skip-repo 2>&1 | tee install.log
+
+  if grep -q "--replace-all" install.log; then
+    echo >&2 "fatal: unexpected git config --replace-all via 'git lfs install'"
+    exit 1
+  fi
 
   [ "$smudge" = "$(git config filter.lfs.smudge)" ]
   [ "$clean" = "$(git config filter.lfs.clean)" ]
index 5b90e50..bd82b4f 100755 (executable)
@@ -12,7 +12,7 @@ begin_test "lock with good ref"
 
   git lfs lock "a.dat" --json 2>&1 | tee lock.json
   if [ "0" -ne "${PIPESTATUS[0]}" ]; then
-    echo >&2 "fatal: expected 'git lfs lock \'a.dat\'' to succeed"
+    echo >&2 "fatal: expected \'git lfs lock \'a.dat\'\' to succeed"
     exit 1
   fi
 
@@ -40,7 +40,7 @@ begin_test "lock with good tracked ref"
 
   git lfs lock "a.dat" --json 2>&1 | tee lock.json
   if [ "0" -ne "${PIPESTATUS[0]}" ]; then
-    echo >&2 "fatal: expected 'git lfs lock \'a.dat\'' to succeed"
+    echo >&2 "fatal: expected \'git lfs lock \'a.dat\'\' to succeed"
     exit 1
   fi
 
@@ -65,7 +65,7 @@ begin_test "lock with bad ref"
 
   GIT_CURL_VERBOSE=1 git lfs lock "a.dat" 2>&1 | tee lock.json
   if [ "0" -eq "${PIPESTATUS[0]}" ]; then
-    echo >&2 "fatal: expected 'git lfs lock \'a.dat\'' to fail"
+    echo >&2 "fatal: expected \'git lfs lock \'a.dat\'\' to fail"
     exit 1
   fi
 
@@ -187,7 +187,7 @@ begin_test "creating a lock (within subdirectory)"
 
   git lfs lock --json "a.dat" | tee lock.json
   if [ "0" -ne "${PIPESTATUS[0]}" ]; then
-    echo >&2 "fatal: expected 'git lfs lock \'a.dat\'' to succeed"
+    echo >&2 "fatal: expected \'git lfs lock \'a.dat\'\' to succeed"
     exit 1
   fi
 
@@ -195,3 +195,37 @@ begin_test "creating a lock (within subdirectory)"
   assert_server_lock "$reponame" "$id"
 )
 end_test
+
+begin_test "creating a lock (symlinked working directory)"
+(
+  set -eo pipefail
+
+  if [[ $(uname) == *"MINGW"* ]]; then
+    echo >&2 "info: skipped on Windows ..."
+    exit 0
+  fi
+
+  reponame="lock-in-symlinked-working-directory"
+  setup_remote_repo "$reponame"
+  clone_repo "$reponame" "$reponame"
+
+  git lfs track -l "*.dat"
+  mkdir -p folder1 folder2
+  printf "hello" > folder2/a.dat
+  add_symlink "../folder2" "folder1/folder2"
+
+  git add --all .
+  git commit -m "initial commit"
+  git push origin master
+
+  pushd "$TRASHDIR" > /dev/null
+    ln -s "$reponame" "$reponame-symlink"
+    cd "$reponame-symlink"
+
+    git lfs lock --json folder1/folder2/a.dat 2>&1 | tee lock.json
+
+    id="$(assert_lock lock.json folder1/folder2/a.dat)"
+    assert_server_lock "$reponame" "$id" master
+  popd > /dev/null
+)
+end_test
index d5974ef..5ceee0d 100755 (executable)
@@ -306,3 +306,26 @@ begin_test "ls-files: reference with --deleted"
   [ 1 -eq $(grep -c "a\.dat" ls-files-deleted.log) ]
 )
 end_test
+
+begin_test "ls-files: invalid --all ordering"
+(
+  set -e
+
+  reponame="ls-files-invalid---all-ordering"
+  git init "$reponame"
+  cd "$reponame"
+
+  git lfs track "*.dat"
+  echo "Hello world" > a.dat
+
+  git add .gitattributes a.dat
+  git commit -m "initial commit"
+
+  git lfs ls-files -- --all 2>&1 | tee ls-files.out
+  if [ ${PIPESTATUS[0]} = "0" ]; then
+    echo >&2 "fatal: expected \`git lfs ls-files -- --all\' to fail"
+    exit 1
+  fi
+  grep "Could not scan for Git LFS tree" ls-files.out
+)
+end_test
diff --git a/test/test-mergetool.sh b/test/test-mergetool.sh
new file mode 100755 (executable)
index 0000000..baac2e8
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+
+. "test/testlib.sh"
+
+begin_test "mergetool works with large files"
+(
+  set -e
+
+  reponame="mergetool-works-with-large-files"
+  git init "$reponame"
+  cd "$reponame"
+
+  git lfs track "*.dat"
+  printf "base" > conflict.dat
+  git add .gitattributes conflict.dat
+  git commit -m "initial commit"
+
+  git checkout -b conflict
+  printf "b" > conflict.dat
+  git add conflict.dat
+  git commit -m "conflict.dat: b"
+
+  git checkout master
+
+  printf "a" > conflict.dat
+  git add conflict.dat
+  git commit -m "conflict.dat: a"
+
+  set +e
+  git merge conflict
+  set -e
+
+  git config mergetool.inspect.cmd '
+    for i in BASE LOCAL REMOTE; do
+      echo "\$$i=$(eval "cat \"\$$i\"")";
+    done;
+    exit 1
+  '
+  git config mergetool.inspect.trustExitCode true
+
+  yes | git mergetool \
+      --no-prompt \
+      --tool=inspect \
+      -- conflict.dat 2>&1 \
+    | tee mergetool.log
+
+  grep "\$BASE=base" mergetool.log
+  grep "\$LOCAL=a" mergetool.log
+  grep "\$REMOTE=b" mergetool.log
+)
+end_test
index e461279..6d3408f 100755 (executable)
@@ -18,7 +18,7 @@ assert_ref_unmoved() {
   fi
 }
 
-# setup_multiple_local_branches creates a repository as follows:
+# setup_local_branch_with_gitattrs creates a repository as follows:
 #
 #   A---B
 #        \
@@ -79,6 +79,27 @@ setup_multiple_local_branches() {
   git checkout master
 }
 
+# setup_local_branch_with_space creates a repository as follows:
+#
+#   A
+#    \
+#     refs/heads/master
+#
+# - Commit 'A' has 50 bytes in a file named "a file.txt".
+setup_local_branch_with_space() {
+  set -e
+
+  reponame="migrate-local-branch-with-space"
+  filename="a file.txt"
+
+  remove_and_create_local_repo "$reponame"
+
+  base64 < /dev/urandom | head -c 50 > "$filename"
+
+  git add "$filename"
+  git commit -m "initial commit"
+}
+
 # setup_single_remote_branch creates a repository as follows:
 #
 #   A---B
@@ -240,6 +261,29 @@ setup_single_local_branch_deep_trees() {
   git commit -m "initial commit"
 }
 
+# setup_local_branch_with_symlink creates a repository as follows:
+#
+#   A
+#    \
+#     refs/heads/master
+#
+# - Commit 'A' has 120, in a.txt, and a symbolic link link.txt to a.txt.
+setup_local_branch_with_symlink() {
+  set -e
+
+  reponame="migrate-single-local-branch-with-symlink"
+
+  remove_and_create_local_repo "$reponame"
+
+  base64 < /dev/urandom | head -c 120 > a.txt
+
+  git add a.txt
+  git commit -m "initial commit"
+
+  add_symlink "a.txt" "link.txt"
+  git commit -m "add symlink"
+}
+
 # make_bare converts the existing full checkout of a repository into a bare one,
 # and then `cd`'s into it.
 make_bare() {
index 1c1ff4b..6adcb09 100755 (executable)
@@ -616,3 +616,43 @@ begin_test "migrate import (handle copies of files)"
   [ "$oid_root" = "$oid_root_after_migration" ]
 )
 end_test
+
+begin_test "migrate import (--include with space)"
+(
+  set -e
+
+  setup_local_branch_with_space
+
+  oid="$(calc_oid "$(git cat-file -p :"a file.txt")")"
+
+  git lfs migrate import --include "a file.txt"
+
+  assert_pointer "refs/heads/master" "a file.txt" "$oid" 50
+  cat .gitattributes
+  if [ 1 -ne "$(grep -c "a\[\[:space:\]\]file.txt" .gitattributes)" ]; then
+    echo >&2 "fatal: expected \"a[[:space:]]file.txt\" to appear in .gitattributes"
+    echo >&2 "fatal: got"
+    sed -e 's/^/  /g' < .gitattributes >&2
+    exit 1
+  fi
+)
+end_test
+
+begin_test "migrate import (handle symbolic link)"
+(
+  set -e
+
+  setup_local_branch_with_symlink
+
+  txt_oid="$(calc_oid "$(git cat-file -p :a.txt)")"
+  link_oid="$(calc_oid "$(git cat-file -p :link.txt)")"
+
+  git lfs migrate import --include="*.txt"
+
+  assert_pointer "refs/heads/master" "a.txt" "$txt_oid" "120"
+
+  assert_local_object "$txt_oid" "120"
+  # "link.txt" is a symbolic link so it should be not in LFS
+  refute_local_object "$link_oid" "5"
+)
+end_test
index 24b9963..be22229 100755 (executable)
@@ -632,6 +632,7 @@ begin_test "push (with invalid object size)"
   set -e
 
   grep "invalid size (got: -1)" push.log
+  [ "0" -eq "$(grep -c "panic" push.log)" ]
   [ "0" -ne "$res" ]
 
   refute_server_object "$reponame" "$(calc_oid "$contents")"
index 65ec342..02ff7ce 100755 (executable)
@@ -540,3 +540,21 @@ begin_test "track (with comments)"
   [ "0" -eq "$(grep -c "\.png" track.log)" ]
 )
 end_test
+
+begin_test "track (with current-directory prefix)"
+(
+  set -e
+
+  reponame="track-with-current-directory-prefix"
+  git init "$reponame"
+  cd "$reponame"
+
+  git lfs track "./a.dat"
+  printf "a" > a.dat
+
+  git add .gitattributes a.dat
+  git commit -m "initial commit"
+
+  grep -e "^a.dat" .gitattributes
+)
+end_test
index 4d588bc..82b0734 100755 (executable)
@@ -169,7 +169,12 @@ begin_test "uninstall --local"
   [ "global clean" = "$(git config --global filter.lfs.clean)" ]
   [ "global filter" = "$(git config --global filter.lfs.process)" ]
 
-  git lfs uninstall --local
+  git lfs uninstall --local 2>&1 | tee uninstall.log
+  if [ ${PIPESTATUS[0]} -ne 0 ]; then
+    echo >&2 "fatal: expected 'git lfs uninstall --local' to succeed"
+    exit 1
+  fi
+  grep -v "Global Git LFS configuration has been removed." uninstall.log
 
   # global configs
   [ "global smudge" = "$(git config --global filter.lfs.smudge)" ]
index e484c9f..2e5005e 100755 (executable)
@@ -72,3 +72,65 @@ begin_test "untrack removes escape sequences"
   assert_attributes_count "\\#" "filter=lfs" 0
 )
 end_test
+
+begin_test "untrack removes prefixed patterns (legacy)"
+(
+  set -e
+
+  reponame="untrack-removes-prefix-patterns-legacy"
+  git init "$reponame"
+  cd "$reponame"
+
+  echo "./a.dat filter=lfs diff=lfs merge=lfs" > .gitattributes
+  printf "a" > a.dat
+  git add .gitattributes a.dat
+  git commit -m "initial commit"
+
+  git lfs untrack "./a.dat"
+
+  if [ ! -z "$(cat .gitattributes)" ]; then
+    echo &>2 "fatal: expected 'git lfs untrack' to clear .gitattributes"
+    exit 1
+  fi
+
+  git checkout -- .gitattributes
+
+  git lfs untrack "a.dat"
+
+  if [ ! -z "$(cat .gitattributes)" ]; then
+    echo &>2 "fatal: expected 'git lfs untrack' to clear .gitattributes"
+    exit 1
+  fi
+)
+end_test
+
+begin_test "untrack removes prefixed patterns (modern)"
+(
+  set -e
+
+  reponame="untrack-removes-prefix-patterns-modern"
+  git init "$reponame"
+  cd "$reponame"
+
+  echo "a.dat filter=lfs diff=lfs merge=lfs" > .gitattributes
+  printf "a" > a.dat
+  git add .gitattributes a.dat
+  git commit -m "initial commit"
+
+  git lfs untrack "./a.dat"
+
+  if [ ! -z "$(cat .gitattributes)" ]; then
+    echo &>2 "fatal: expected 'git lfs untrack' to clear .gitattributes"
+    exit 1
+  fi
+
+  git checkout -- .gitattributes
+
+  git lfs untrack "a.dat"
+
+  if [ ! -z "$(cat .gitattributes)" ]; then
+    echo &>2 "fatal: expected 'git lfs untrack' to clear .gitattributes"
+    exit 1
+  fi
+)
+end_test
diff --git a/test/test-version.sh b/test/test-version.sh
new file mode 100755 (executable)
index 0000000..31dfdb7
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+
+. "test/testlib.sh"
+
+begin_test "git lfs --version is a synonym of git lfs version"
+(
+  set -e
+
+  reponame="git-lfs-version-synonymous"
+  mkdir "$reponame"
+  cd "$reponame"
+
+  git lfs version 2>&1 >version.log
+  git lfs --version 2>&1 >flag.log
+
+  if [ "$(cat version.log)" != "$(cat flag.log)" ]; then
+    echo >&2 "fatal: expected 'git lfs version' and 'git lfs --version' to"
+    echo >&2 "produce identical output ..."
+
+    diff -u {version,flag}.log
+  fi
+)
+end_test
index 2b47536..d49f99a 100755 (executable)
@@ -46,7 +46,7 @@ UploadTransfers=basic
 $(escape_path "$(env | grep "^GIT")")
 %s
 " "$(git lfs version)" "$(git version)" "$envInitConfig")
-    actual=$(git lfs env)
+    actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
     contains_same_elements "$expected" "$actual"
 
     worktreename="worktree-2"
@@ -82,7 +82,7 @@ UploadTransfers=basic
 $(escape_path "$(env | grep "^GIT")")
 %s
 " "$(git lfs version)" "$(git version)" "$envInitConfig")
-    actual=$(git lfs env)
+    actual=$(git lfs env | grep -v "^GIT_EXEC_PATH=")
     contains_same_elements "$expected" "$actual"
 )
 end_test
index bb8fdf8..4018093 100644 (file)
@@ -738,3 +738,14 @@ has_test_dir() {
     exit 0
   fi
 }
+
+add_symlink() {
+  local src=$1
+  local dest=$2
+
+  prefix=`git rev-parse --show-prefix`
+  hashsrc=`printf "$src" | git hash-object -w --stdin`
+
+  git update-index --add --cacheinfo 120000 "$hashsrc" "$prefix$dest"
+  git checkout -- "$dest"
+}
index 0c18599..a42a2cb 100644 (file)
@@ -2,6 +2,7 @@ package tq
 
 import (
        "fmt"
+       "math"
        "os"
        "path/filepath"
        "strings"
@@ -238,8 +239,30 @@ func (m *Meter) str() string {
                direction,
                percentage,
                m.finishedFiles, m.estimatedFiles,
-               humanize.FormatBytes(uint64(m.currentBytes)),
-               humanize.FormatByteRate(uint64(m.avgBytes), time.Second))
+               humanize.FormatBytes(clamp(m.currentBytes)),
+               humanize.FormatByteRate(clampf(m.avgBytes), time.Second))
+}
+
+// clamp clamps the given "x" within the acceptable domain of the uint64 integer
+// type, so as to prevent over- and underflow.
+func clamp(x int64) uint64 {
+       if x < 0 {
+               return 0
+       }
+       if x > math.MaxInt64 {
+               return math.MaxUint64
+       }
+       return uint64(x)
+}
+
+func clampf(x float64) uint64 {
+       if x < 0 {
+               return 0
+       }
+       if x > math.MaxUint64 {
+               return math.MaxUint64
+       }
+       return uint64(x)
 }
 
 func (m *Meter) logBytes(direction, name string, read, total int64) {
index 3f3874d..45692d6 100644 (file)
@@ -4,7 +4,7 @@
                "FileVersion": {
                        "Major": 2,
                        "Minor": 4,
-                       "Patch": 0,
+                       "Patch": 1,
                        "Build": 0
                }
        },
@@ -13,7 +13,7 @@
                "FileDescription": "Git LFS",
                "LegalCopyright": "GitHub, Inc. and Git LFS contributors",
                "ProductName": "Git Large File Storage (LFS)",
-               "ProductVersion": "2.4.0"
+               "ProductVersion": "2.4.1"
        },
        "IconPath": "script/windows-installer/git-lfs-logo.ico"
 }