Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

git mv fails (assertion failure) #2920

Closed
1 task done
DRNadler opened this issue Dec 1, 2020 · 23 comments
Closed
1 task done

git mv fails (assertion failure) #2920

DRNadler opened this issue Dec 1, 2020 · 23 comments
Labels
case-insensitive-fs Bug related to case-insensitive file systems question unclear

Comments

@DRNadler
Copy link

DRNadler commented Dec 1, 2020

  • I was not able to find an open or closed issue matching what I'm seeing

Setup

  • git version 2.29.2.windows 64-bit
$ git --version --build-options

git --version --build-options
git version 2.29.2.windows.2
cpu: x86_64
built from commit: 3464b98ce6803c98bf8fb34390cd150d66e4a0d3
sizeof-long: 4
sizeof-size_t: 8
shell-path: /bin/sh```

 - Windows 10 64-bit

$ cmd.exe /c ver

Microsoft Windows [Version 10.0.19041.630]


 - Default installation options 

type "C:\Program Files\Git\etc\install-options.txt"
Editor Option: Nano
Custom Editor Path:
Default Branch Option:
Path Option: Cmd
SSH Option: OpenSSH
Tortoise Option: false
CURL Option: OpenSSL
CRLF Option: CRLFCommitAsIs
Bash Terminal Option: MinTTY
Git Pull Behavior Option: Merge
Use Credential Manager: Core
Performance Tweaks FSCache: Enabled
Enable Symlinks: Disabled
Enable Pseudo Console Support: Disabled

Details

File I am trying to MV is committed.
Target directory is under source control and contains committed files.
git deleted source file but failed to add target.

c:\git_CAP\repo_MFW2>git mv mfw2/application/phaseCalc_IQ_Xiong.hpp PC_phaseTest/phaseCalc_IQ_Xiong.hpp
Assertion failed: pos >= 0, file builtin/mv.c, line 295

c:\git_CAP\repo_MFW2>git status mfw2/application/phaseCalc_IQ_Xiong.hpp
On branch master
nothing to commit, working tree clean

c:\git_CAP\repo_MFW2>git status PC_phaseTest
On branch master
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git restore ..." to discard changes in working directory)
modified: PC_phaseTest/InitialWorkingOutput.txt
modified: PC_phaseTest/PC_phaseTest.cpp

Untracked files:
(use "git add ..." to include in what will be committed)
PC_phaseTest/phaseCalc_IQ_Xiong.hpp

no changes added to commit (use "git add" and/or "git commit -a")

c:\git_CAP\repo_MFW2>git status
On branch master
Changes not staged for commit:
(use "git add/rm ..." to update what will be committed)
(use "git restore ..." to discard changes in working directory)
modified: MFW2/application/phaseCalc_IQ_Dave.cpp
deleted: MFW2/application/phaseCalc_IQ_Xiong.hpp
modified: PC_phaseTest/InitialWorkingOutput.txt

Untracked files:
(use "git add ..." to include in what will be committed)
PC_phaseTest/phaseCalc_IQ_Xiong.hpp

no changes added to commit (use "git add" and/or "git commit -a")

@dscho
Copy link
Member

dscho commented Dec 1, 2020

Please work on providing a Minimal, Complete & Verifiable Example. It will dramatically increase your chances to receive helpful assistance.

What you provided is neither minimal, nor complete, as you have not made your repository publicly accessible.

@dscho
Copy link
Member

dscho commented Dec 1, 2020

Having said that...

c:\git_CAP\repo_MFW2>git mv mfw2/application/phaseCalc_IQ_Xiong.hpp PC_phaseTest/phaseCalc_IQ_Xiong.hpp
Assertion failed: pos >= 0, file builtin/mv.c, line 295

[...]
c:\git_CAP\repo_MFW2>git status
[...]
deleted: MFW2/application/phaseCalc_IQ_Xiong.hpp

Please note that the Git index is not case-insensitive. So while you may access the directory on disk via mfw2, you cannot access the same directory in the index via mfw2: it is clearly recorded as MFW2. I bet it would work if you called this command instead:

git mv MFW2/application/phaseCalc_IQ_Xiong.hpp PC_phaseTest/

@dscho dscho added case-insensitive-fs Bug related to case-insensitive file systems question unclear labels Dec 1, 2020
@dscho
Copy link
Member

dscho commented Dec 8, 2020

I will assume that the suggested git mv invocation fixed the problem.

@dscho dscho closed this as completed Dec 8, 2020
@danmoseley
Copy link

danmoseley commented Dec 21, 2020

In case it's helpful, here's a trivial repro as I hit this crash also:

C:\git\demo>git --version --build-options
git version 2.29.2.windows.3
cpu: x86_64
built from commit: d054eb1fc46ff23e7c95756a7c747e2f2864b478
sizeof-long: 4
sizeof-size_t: 8
shell-path: /bin/sh

C:\git\demo>git init
Initialized empty Git repository in C:/git/demo/.git/

C:\git\demo>echo foo > foo

C:\git\demo>git add foo

C:\git\demo>git commit -am "add foo"
[master (root-commit) 3dbfa54] add foo
 1 file changed, 1 insertion(+)
 create mode 100644 foo

C:\git\demo>git mv foo FOO

C:\git\demo>git mv foo BAR
Assertion failed: pos >= 0, file builtin/mv.c, line 295

At this point git crashes and leaves .git/index.lock behind.

This is on Windows 10 20H2.

@PhilipOakley
Copy link

here's a trivial repro

This is on Windows 10 20H2.

Quick clarification: Have you been able to test the repo on WSL/WSL2 or Linux (i.e. did you check if it was an upstream Git problem or special to Git for Windows)?

@tboegi
Copy link

tboegi commented Dec 23, 2020

No need to test WSL or whatever, as far as I can see.
Same problem exist under MacOS.
Here is a possible fix, with mangled whitespace

git diff
diff --git a/builtin/mv.c b/builtin/mv.c
index 7dac714af9..48d0c930a2 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -292,8 +292,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
continue;

pos = cache_name_pos(src, strlen(src));
- assert(pos >= 0);
- rename_cache_entry_at(pos, dst);
+ if (pos >= 0)
+ rename_cache_entry_at(pos, dst);
+ else if (!ignore_errors)
+ die(_("bad source: source=%s, destination=%s"),
+ src, dst);
}

if (gitmodules_modified)

@danmoseley
Copy link

@tboegi do you plan to contribute that fix?

@tboegi
Copy link

tboegi commented Dec 23, 2020

@danmosemsft :
I have no plans to send it to git.git, at least not for the next 4 weeks or so.
If someone wants to pick it up: Please go ahead.

@danmoseley
Copy link

Trying 2.30.0.rc2 on Ubuntu 20.04 I get a nice error instead

dan@LAPTOP-P6UJDVTA:/tmp/tmp$ git mv foo FOO
dan@LAPTOP-P6UJDVTA:/tmp/tmp$ git mv foo BAR
fatal: bad source, source=foo, destination=BAR

I assume it repros on Linux if it is possible to have a case insensitive file system.

@danmoseley
Copy link

danmoseley commented Dec 28, 2020

I'm happy to submit a fix, but I have a question about how to test it. I was expecting the exit code when the assert happens to be 128+SIGABRT = 134, however it is 3:

danmose@LAPTOP-P6UJDVTA MINGW64 ~/tmp (master)
$ git mv foo BAR
Assertion failed: pos >= 0, file builtin/mv.c, line 295

danmose@LAPTOP-P6UJDVTA MINGW64 ~/tmp (master)
$ echo $?
3

In my test, I have test_must_fail git mv foo BAR and test_must_fail considers 3 an acceptable failure, whereas I want test_must_fail to reject it with the message test_must_fail: died by signal 6: .... as it would do for 134.

My knowledge of signals/shell is limited. What am I missing?

@danmoseley
Copy link

Ah, I see this is MSVCRT default behavior to exit(3) on a failed assertion. Does that mean when running the tests on Windows, test_must_fail will ignore failed assertions?

I will just do test_expect_code 128 git mv foo BAR

@dscho
Copy link
Member

dscho commented Dec 29, 2020

Ah, I see this is MSVCRT default behavior to exit(3) on a failed assertion. Does that mean when running the tests on Windows, test_must_fail will ignore failed assertions?

I will just do test_expect_code 128 git mv foo BAR

It means that we probably don't test assertions in the test suite. Typically, assert() is used for conditions that the author expects never to happen, so it is relatively easy to see why.

Besides, there is a trend to remove assert() calls in Git's source code, sometimes replacing them by BUG() calls.

@danmoseley
Copy link

so it is relatively easy to see why.

My observation was that an assert within test_must_fail will not fail the test so maybe the assert will be undetected. But, if they're being removed, no matter.

@dscho
Copy link
Member

dscho commented Dec 30, 2020

so it is relatively easy to see why.

My observation was that an assert within test_must_fail will not fail the test

I wonder how one would set up a test case that runs into a situation expected never to happen (and only defensively guarded via an assert() call).

so maybe the assert will be undetected. But, if they're being removed, no matter.

@danmoseley
Copy link

I wonder how one would set up a test case that runs into a situation expected never to happen (and only defensively guarded via an assert() call

Presumably any test could in theory hit an assert, just as it could hit a segfault. It might be configuration specific, non deterministic, etc.

@dscho
Copy link
Member

dscho commented Dec 30, 2020

I wonder how one would set up a test case that runs into a situation expected never to happen (and only defensively guarded via an assert() call

Presumably any test could in theory hit an assert, just as it could hit a segfault. It might be configuration specific, non deterministic, etc.

test_must_fail is reserved for known bugs in Git, though.

@danmoseley
Copy link

Seems like it's being used to test cases where git commands are supposed to fail for bad input? eg

test_expect_success \
    'do not move directory over existing directory' \
    'mkdir path0 && mkdir path0/path2 && test_must_fail git mv path2 path0'
test_expect_success '"add non-existent" should fail' '
	test_must_fail git add non-existent &&

README says the purpose of test_must_fail is to verify a git command fails without missing segv's as ! would do.

@dscho
Copy link
Member

dscho commented Dec 31, 2020

Whoops, I confused test_expect_failure with test_must_fail (again! Grmbl).

Having said that, test_must_fail intends to verify that Git errors out when it should.

An assert() is very different from an expected error message: typically, those statements are skipped when compiling in release mode. Those calls are purely intended to help developing code, to verify invariants. I'm still puzzled, therefore, why anybody would want to test for assert() in the test suite. It strikes me as a case of "not holding the tools correctly".

@PhilipOakley
Copy link

I'm still puzzled, therefore, why anybody would want to test for assert() in the test suite.

Hi @dscho, isn't that a catch 22 problem.

An assert without a test only has a vain hope of triggering, so the 'developer' is left in a bit of no-man's land where production (release) code will wander off into some seg fault because the check is skipped, while the maintaining dev does want to know how to try and trigger the fault.

Not sure there's an easy solution. Maybe those tests could have a 'DEVELOPER' pre-req? Just a thought.

@dscho
Copy link
Member

dscho commented Dec 31, 2020

I'm still puzzled, therefore, why anybody would want to test for assert() in the test suite.

Hi @dscho, isn't that a catch 22 problem.

No.

I fear you're mistaking assert() for something it is not.

All you're supposed to do with that construct is verify invariants, things you (the developer) take for granted, e.g. that a given array is sorted (assert(i + 1 == nr || strcmp(array[i], array[i+1]) <= 0)). If such a basic contract is violated, you want the debugger to stop right there. That's what assertions are all about.

If you need to add a regression test to verify that your invariant testing is sound, you need to think about your life choices :-)

An assert without a test only has a vain hope of triggering, so the 'developer' is left in a bit of no-man's land where production (release) code will wander off into some seg fault because the check is skipped, while the maintaining dev does want to know how to try and trigger the fault.

If you're relying on assert() to prevent a segmentation fault, you're definitely not using that construct correctly.

Again, this is a function intended to verify invariants while developing the code in question (or while debugging it). That's all assert() ever was. Really.

@PhilipOakley
Copy link

that construct is verify invariants, things you (the developer) take for granted

So, by that argument, this assert never triggered .... which is why it's a catch 22 at this level.

@dscho
Copy link
Member

dscho commented Dec 31, 2020

Why are we even talking about this anymore? The fact that an assert() should not trigger is by design.

@tboegi
Copy link

tboegi commented Mar 1, 2021

This very assert should not trigger at all - a patch has been send upstream.

gitster pushed a commit to git/git that referenced this issue Mar 4, 2021
The following sequence, on a case-insensitive file system,
(strictly speeking with core.ignorecase=true)
leads to an assertion failure and leaves .git/index.lock behind.

git init
echo foo >foo
git add foo
git mv foo FOO
git mv foo bar

This regression was introduced in Commit 9b906af,
"git-mv: improve error message for conflicted file"

The bugfix is to change the "file exist case-insensitive in the index"
into the correct "file exist (case-sensitive) in the index".

This avoids the "assert" later in the code and keeps setting up the
"ce" pointer for ce_stage(ce) done in the next else if.

This fixes
git-for-windows#2920

Reported-By: Dan Moseley <Dan.Moseley@microsoft.com>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
case-insensitive-fs Bug related to case-insensitive file systems question unclear
Projects
None yet
Development

No branches or pull requests

5 participants