# Git LFS Changelog
+## 2.3.2 (3 October, 2017)
+
+### Features
+
+* bump default activity timeout from 10s -> 30s #2632 (@technoweenie)
+
+### Bugs
+
+* ensure files are marked readonly after unlocking by ID #2642 (@technoweenie)
+* add files to index with path relative to current dir #2641 (@technoweenie)
+* better Netrc errors #2633 (@technoweenie)
+* only use askpass if credential.helper is not configured #2637 (@technoweenie)
+* convert backslash to slash when writing to .gitattributes #2625 (@technoweenie)
+
+### Misc
+
+* only copy req headers if there are git-configured extra headers #2622 (@technoweenie)
+* update tracerx to add timestamps #2620 (@rubyist)
+
## 2.3.1 (27 September, 2017)
### Features
writeablePatterns = append(writeablePatterns, pattern)
}
- Print("Tracking %q", pattern)
+ Print("Tracking %q", unescapeTrackPattern(encodedArg))
}
// Now read the whole local attributes file and iterate over the contents,
)
func escapeTrackPattern(unescaped string) string {
- var escaped string = unescaped
+ var escaped string = strings.Replace(unescaped, `\`, "/", -1)
for from, to := range trackEscapePatterns {
escaped = strings.Replace(escaped, from, to, -1)
return apiClient
}
+func closeAPIClient() error {
+ global.Lock()
+ defer global.Unlock()
+ if apiClient == nil {
+ return nil
+ }
+ return apiClient.Close()
+}
+
func newLockClient(remote string) *locking.Client {
storageConfig := config.Config.StorageConfig()
lockClient, err := locking.NewClient(remote, getAPIClient())
}
func (c *singleCheckout) Run(p *lfs.WrappedPointer) {
+ cwdfilepath := c.pathConverter.Convert(p.Name)
+
// Check the content - either missing or still this pointer (not exist is ok)
- filepointer, err := lfs.DecodePointerFromFile(p.Name)
+ filepointer, err := lfs.DecodePointerFromFile(cwdfilepath)
if err != nil && !os.IsNotExist(err) {
if errors.IsNotAPointerError(err) {
// File has non-pointer content, leave it alone
return
}
- cwdfilepath := c.pathConverter.Convert(p.Name)
-
err = lfs.PointerSmudgeToFile(cwdfilepath, p.Pointer, false, c.manifest, nil)
if err != nil {
if errors.IsDownloadDeclinedError(err) {
}
root.Execute()
- getAPIClient().Close()
+ closeAPIClient()
}
func gitlfsCommand(cmd *cobra.Command, args []string) {
)
const (
- Version = "2.3.1"
+ Version = "2.3.2"
)
func init() {
+git-lfs (2.3.2) stable; urgency=low
+
+ * New upstream version
+
+ -- Rick Olson <technoweenie@gmail.com> Tue, 3 Oct 2017 14:29:00 +0000
+
git-lfs (2.3.1) stable; urgency=low
* New upstream version
Sets the maximum time, in seconds, that the HTTP client will wait for the
next tcp read or write. If < 1, no activity timeout is used at all.
- Default: 10 seconds
+ Default: 30 seconds
* `lfs.keepalive`
-hash: 7ac0f055e06001b80cc9cfc40de07c103b3b59abec0f3be0fca7cce49b71eec7
-updated: 2017-09-19T15:38:52.833333079-06:00
+hash: e19b925b9eaca9a10a7742b4a4b1dc8047bff437584538dda59f4f10e69fa6ca
+updated: 2017-09-27T12:34:48.032089491-04:00
imports:
- name: github.com/bgentry/go-netrc
version: 9fd32a8b3d3d3f9d43c341bfe098430e07609480
- name: github.com/pkg/errors
version: c605e284fe17294bda444b34710735b29d1a9d90
- name: github.com/rubyist/tracerx
- version: d7bcc0bc315bed2a841841bee5dbecc8d7d7582f
+ version: 787959303086f44a8c361240dfac53d3e9d53ed2
- name: github.com/spf13/cobra
version: c55cdf33856a08e4822738728b41783292812889
- name: github.com/spf13/pflag
- package: github.com/olekukonko/ts
version: ecf753e7c962639ab5a1fb46f7da627d4c0a04b8
- package: github.com/rubyist/tracerx
- version: d7bcc0bc315bed2a841841bee5dbecc8d7d7582f
+ version: 787959303086f44a8c361240dfac53d3e9d53ed2
- package: github.com/spf13/cobra
version: c55cdf33856a08e4822738728b41783292812889
- package: github.com/spf13/pflag
}
func (c *Client) extraHeadersFor(req *http.Request) http.Header {
+ extraHeaders := c.extraHeaders(req.URL)
+ if len(extraHeaders) == 0 {
+ return req.Header
+ }
+
copy := make(http.Header, len(req.Header))
for k, vs := range req.Header {
copy[k] = vs
}
- for k, vs := range c.extraHeaders(req.URL) {
+ for k, vs := range extraHeaders {
for _, v := range vs {
copy[k] = append(copy[k], v)
}
MaxIdleConnsPerHost: concurrentTransfers,
}
- activityTimeout := 10
+ activityTimeout := 30
if v, ok := c.uc.Get("lfs", fmt.Sprintf("https://%v", host), "activitytimeout"); ok {
if i, err := strconv.Atoi(v); err == nil {
activityTimeout = i
// See: https://git-scm.com/docs/gitcredentials#_requesting_credentials
// for more.
AskPass string `os:"GIT_ASKPASS" git:"core.askpass" os:"SSH_ASKPASS"`
+ // Helper is a string defining the credential helper that Git should use.
+ Helper string `git:"credential.helper"`
// Cached is a boolean determining whether or not to enable the
// credential cacher.
Cached bool `git:"lfs.cachecredentials"`
}
var hs []CredentialHelper
- if len(ccfg.AskPass) > 0 {
+ if len(ccfg.Helper) == 0 && len(ccfg.AskPass) > 0 {
hs = append(hs, &AskPassCredentialHelper{
Program: ccfg.AskPass,
})
}
// Approve implements CredentialHelper.Approve and approves the given Creds
-// "what" amongst all knonw CredentialHelpers. If any `CredentialHelper`s
+// "what" amongst all known CredentialHelpers. If any `CredentialHelper`s
// returned a non-nil error, no further `CredentialHelper`s are notified, so as
// to prevent inconsistent state.
func (h CredentialHelpers) Approve(what Creds) error {
gitEnv = make(TestEnv)
}
- netrc, err := ParseNetrc(osEnv)
+ netrc, netrcfile, err := ParseNetrc(osEnv)
if err != nil {
- return nil, err
+ return nil, errors.Wrap(err, fmt.Sprintf("bad netrc file %s", netrcfile))
}
httpsProxy, httpProxy, noProxy := getProxyServers(osEnv, gitEnv)
FindMachine(string) *netrc.Machine
}
-func ParseNetrc(osEnv Env) (NetrcFinder, error) {
+func ParseNetrc(osEnv Env) (NetrcFinder, string, error) {
home, _ := osEnv.Get("HOME")
if len(home) == 0 {
- return &noFinder{}, nil
+ return &noFinder{}, "", nil
}
nrcfilename := filepath.Join(home, netrcBasename)
if _, err := os.Stat(nrcfilename); err != nil {
- return &noFinder{}, nil
+ return &noFinder{}, nrcfilename, nil
}
- return netrc.ParseFile(nrcfilename)
+ f, err := netrc.ParseFile(nrcfilename)
+ return f, nrcfilename, err
}
type noFinder struct{}
// Internal function to repopulate lockable patterns
// You must have locked the c.lockableMutex in the caller
func (c *Client) refreshLockablePatterns() {
-
paths := git.GetAttributePaths(c.LocalWorkingDir, c.LocalGitDir)
// Always make non-nil even if empty
c.lockablePatterns = make([]string, 0, len(paths))
return fmt.Errorf("Unable to get lock id: %v", err)
}
- err = c.UnlockFileById(id, force)
- if err != nil {
- return err
- }
-
- abs, err := getAbsolutePath(path)
- if err != nil {
- return errors.Wrap(err, "make lockpath absolute")
- }
-
- // Make non-writeable if required
- if c.SetLockableFilesReadOnly && c.IsFileLockable(path) {
- return tools.SetFileWriteFlag(abs, false)
- }
- return nil
-
+ return c.UnlockFileById(id, force)
}
// UnlockFileById attempts to unlock a lock with a given id on the current remote
return fmt.Errorf("Error caching unlock information: %v", err)
}
+ if unlockRes.Lock != nil {
+ abs, err := getAbsolutePath(unlockRes.Lock.Path)
+ if err != nil {
+ return errors.Wrap(err, "make lockpath absolute")
+ }
+
+ // Make non-writeable if required
+ if c.SetLockableFilesReadOnly && c.IsFileLockable(unlockRes.Lock.Path) {
+ return tools.SetFileWriteFlag(abs, false)
+ }
+ }
+
return nil
}
Name: git-lfs
-Version: 2.3.1
+Version: 2.3.2
Release: 1%{?dist}
Summary: Git extension for versioning large files
# $password is defined from test/cmd/lfstest-gitserver.go (see: skipIfBadAuth)
export LFS_ASKPASS_USERNAME="user"
export LFS_ASKPASS_PASSWORD="pass"
+ git config "credential.helper" ""
GIT_ASKPASS="lfs-askpass" SSH_ASKPASS="dont-call-me" GIT_TRACE=1 GIT_CURL_VERBOSE=1 git push origin master 2>&1 | tee push.log
GITSERVER_USER="$(printf $GITSERVER | sed -e 's/http:\/\//http:\/\/user@/')"
# $password is defined from test/cmd/lfstest-gitserver.go (see: skipIfBadAuth)
export LFS_ASKPASS_PASSWORD="pass"
+ git config "credential.helper" ""
git config "core.askPass" "lfs-askpass"
cat .git/config
SSH_ASKPASS="dont-call-me" GIT_TRACE=1 GIT_CURL_VERBOSE=1 git push origin master 2>&1 | tee push.log
# $password is defined from test/cmd/lfstest-gitserver.go (see: skipIfBadAuth)
export LFS_ASKPASS_USERNAME="user"
export LFS_ASKPASS_PASSWORD="pass"
+ git config "credential.helper" ""
SSH_ASKPASS="lfs-askpass" GIT_TRACE=1 GIT_CURL_VERBOSE=1 git push origin master 2>&1 | tee push.log
GITSERVER_USER="$(printf $GITSERVER | sed -e 's/http:\/\//http:\/\/user@/')"
contents_oid=$(calc_oid "$contents")
contents2="A"
contents2_oid=$(calc_oid "$contents2")
+ contents3="dir"
+ contents3_oid=$(calc_oid "$contents3")
+ mkdir dir
+ echo "*.log" > .gitignore
printf "$contents" > a.dat
printf "$contents2" > á.dat
- git add a.dat á.dat .gitattributes
+ printf "$contents3" > dir/dir.dat
+ git add .
git commit -m "add files" 2>&1 | tee commit.log
grep "master (root-commit)" commit.log
- grep "3 files changed" commit.log
+ grep "5 files changed" commit.log
grep "create mode 100644 a.dat" commit.log
grep "create mode 100644 .gitattributes" commit.log
ls -al
[ "a" = "$(cat a.dat)" ]
[ "A" = "$(cat "á.dat")" ]
+ [ "dir" = "$(cat "dir/dir.dat")" ]
assert_pointer "master" "a.dat" "$contents_oid" 1
assert_pointer "master" "á.dat" "$contents2_oid" 1
+ assert_pointer "master" "dir/dir.dat" "$contents3_oid" 3
refute_server_object "$reponame" "$contents_oid"
refute_server_object "$reponame" "$contents2_oid"
+ refute_server_object "$reponame" "$contents33oid"
echo "initial push"
git push origin master 2>&1 | tee push.log
- grep "(2 of 2 files)" push.log
+ grep "(3 of 3 files)" push.log
grep "master -> master" push.log
assert_server_object "$reponame" "$contents_oid"
assert_server_object "$reponame" "$contents2_oid"
+ assert_server_object "$reponame" "$contents3_oid"
# change to the clone's working directory
cd ../clone
assert_local_object "$contents_oid" 1
assert_local_object "$contents2_oid" 1
+ assert_clean_status
echo "lfs pull"
- rm a.dat á.dat
+ rm -r a.dat á.dat dir # removing files makes the status dirty
rm -rf .git/lfs/objects
- git lfs pull 2>&1 | grep "(2 of 2 files)"
+ git lfs pull 2>&1 | grep "(3 of 3 files)"
ls -al
[ "a" = "$(cat a.dat)" ]
[ "A" = "$(cat "á.dat")" ]
assert_local_object "$contents2_oid" 1
echo "lfs pull with remote"
- rm a.dat á.dat
+ rm -r a.dat á.dat dir
rm -rf .git/lfs/objects
- git lfs pull origin 2>&1 | grep "(2 of 2 files)"
+ git lfs pull origin 2>&1 | grep "(3 of 3 files)"
[ "a" = "$(cat a.dat)" ]
[ "A" = "$(cat "á.dat")" ]
assert_local_object "$contents_oid" 1
rm -rf .git/lfs/objects
git lfs pull --exclude="a*"
refute_local_object "$contents_oid"
+
+ echo "resetting to test status"
+ git reset --hard
+ assert_clean_status
+
+ echo "lfs pull clean status"
+ git lfs pull
+ assert_clean_status
+
+ echo "lfs pull with -I"
+ git lfs pull -I "*.dat"
+ assert_clean_status
+
+ echo "lfs pull in subdir"
+ cd dir
+ git lfs pull
+ assert_clean_status
+
+ echo "lfs pull in subdir with -I"
+ git lfs pull -I "*.dat"
+ assert_clean_status
)
end_test
cd dir
git init
- git lfs track "foo bar/*"
+ git lfs track "foo bar\\*" | tee track.txt
+ [ "foo[[:space:]]bar/* filter=lfs diff=lfs merge=lfs -text" = "$(cat .gitattributes)" ]
+ [ "Tracking \"foo bar/*\"" = "$(cat track.txt)" ]
mkdir "foo bar"
echo "a" > "foo bar/a"
assert_pointer "master" "foo bar/a" "87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7" 2
assert_pointer "master" "foo bar/b" "0263829989b6fd954f72baaf2fc64bc2e2f01d692d4de72986ea808f6e99813f" 2
)
+end_test
begin_test "track without trailing linebreak"
(
)
end_test
-begin_test "unlocking a file makes ignores readonly"
+begin_test "unlocking a file ignores readonly"
(
set -e
set -e
reponame="unlock_by_id"
- setup_remote_repo_with_file "unlock_by_id" "d.dat"
+ setup_remote_repo_with_file "$reponame" "d.dat"
git lfs lock --json "d.dat" | tee lock.log
+ assert_file_writeable d.dat
id=$(assert_lock lock.log d.dat)
assert_server_lock "$reponame" "$id"
git lfs unlock --id="$id"
- refute_server_lock "$reponame" "$id"
+ refute_file_writeable d.dat
)
end_test
[ -x "$git_root/hooks/pre-push" ]
}
+assert_clean_status() {
+ status="$(git status)"
+ echo "$status" | grep "working tree clean" || {
+ echo $status
+ git lfs status
+ }
+}
+
# pointer returns a string Git LFS pointer file.
#
# $ pointer abc-some-oid 123 <version>
echo "$out"
}
-
# clone_repo_ssl clones a repository from the test Git server to the subdirectory
# $dir under $TRASHDIR, using the SSL endpoint.
# setup_remote_repo() needs to be run first. Output is written to clone_ssl.log.
func PrintfKey(key, format string, args ...interface{}) {
tracer := getTracer(key)
if tracer.enabled {
- fmt.Fprintf(tracer.w, Prefix+format+"\n", args...)
+ fmt.Fprintf(tracer.w, time.Now().Format("15:04:05.000000 ")+Prefix+format+"\n", args...)
return
}
}
if tracer.performance {
since := time.Since(t)
- fmt.Fprintf(tracer.w, "performance %s: %.9f s\n", what, since.Seconds())
+ fmt.Fprintf(tracer.w, time.Now().Format("15:04:05.000000 ")+"performance %s: %.9f s\n", what, since.Seconds())
}
}
"FileVersion": {
"Major": 2,
"Minor": 3,
- "Patch": 1,
+ "Patch": 2,
"Build": 0
}
},