The documented behavior of hg update is: when hg update needs to write a file, but there's already untracked/ignored file by that name, it complains (assuming the contents are not the same). This is controlled by the config options merge.checkignored and merge.checkunknown. Unrelatedly, until 4.3 inclusive, hg update would always fail when there's a directory at a path where the destination has a tracked file. 4.4 contains some changes to improve this, which AFAICT, are not documented at a high level (need to look at commit messages), so it's hard to understand what behavior is intended. Now the problem: the observed behavior since 4.4 is that hg deletes untracked and ignored files with no backup, in the cases where hg update used to fail, so no respect for the merge.check* config options. I don't care so much about the ignored files, but deleting untracked files seems pretty bad. And I worry that even the behavior on ignored files is subject to change (if someone that does care reports this) because it's not really the documented behavior. There's also some weirdness where one use of hg update used to break, then deleted files, then starting breaking again. I made the test below to show concretely the behavior I'm talking about. Ideally, hg update's should retain its ability to work in the face of these file/dir issue (this has caused much pain), but either the current behavior would get documented if it is in fact intended, or perhaps the way to avoid these update failures should change (rename directories that get in the way with a .conflict-with-tracked suffix?). $ hg init r $ cd r $ cat > .hgignore <<EOF > syntax: glob > *.orig > *.ignored > EOF $ hg commit -A -m0 -q $ echo a > a $ echo a.ignored > a.ignored $ hg add a.ignored $ hg commit -A -m1 -q $ hg rm a; mkdir a; echo a/something > a/something $ hg commit -A -m2 -q $ hg up -r 0 -q $ cd .. $ show () { > hg st -A | grep -v hgignore > echo - > hg st -A -n | grep -v hgignore | xargs -r grep -H '' > } @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GOOD untracked file -> tracked file conflict @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ $ hg clone -q r r2 -u 0 && cd r2 $ echo important-stuff > a $ hg up -q 1 a: untracked file differs abort: untracked files in working directory differ from files in requested revision [255] $ show ? a - a:important-stuff $ cd ../ && rm -rf r2 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ GOOD ignored file -> tracked file conflict @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ $ hg clone -q r r2 -u 0 && cd r2 $ echo important-stuff > a.ignored $ hg up -q -r 1 a.ignored: untracked file differs abort: untracked files in working directory differ from files in requested revision [255] $ show I a.ignored - a.ignored:important-stuff $ cd ../ && rm -rf r2 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ BEHAVIOR KEEPS CHANGING untracked file -> tracked dir conflict @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ # 4.7 4.6.2 4.3.2 $ hg clone -q r r2 -u 0 && cd r2 $ echo important-stuff > a $ hg up -q 2 abort: $ENOTDIR$: '$TESTTMP/r2/a/something' [255] $ show ? a I a.ignored - a:important-stuff a.ignored:a.ignored $ cd ../ && rm -rf r2 # # 4.5.2 4.4.2 # $ hg clone -q r r2 -u 0 && cd r2 # $ echo important-stuff > a # $ hg up -q 2 # $ show # C a.ignored # C a/something # - # a.ignored:a.ignored # a/something:a/something # $ cd ../ && rm -rf r2 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ DELETE FILE WITH NO BACKUP untracked file in dir -> dir is replaced by file @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ $ hg clone -q r r2 -u 2 && cd r2 $ echo important-stuff > a/untracked $ hg up -r 1 1 files updated, 0 files merged, 1 files removed, 0 files unresolved $ show C a C a.ignored - a:a a.ignored:a.ignored $ cd ../ && rm -rf r2 # # 4.3.2 # $ hg clone -q r r2 -u 2 && cd r2 # $ echo important-stuff > a/untracked # $ hg up -r 1 # abort: Directory not empty: '$TESTTMP/r2/a' # [255] # $ show # ! a/something # ? a/untracked # C a.ignored # - # grep: a/something: No such file or directory # a/untracked:important-stuff # a.ignored:a.ignored # [123] # $ cd ../ && rm -rf r2 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ DELETE FILE WITH NO BACKUP ignored file in dir -> dir is replaced by file @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ $ hg clone -q r r2 -u 2 && cd r2 $ echo important-stuff > a/bla.ignored $ hg up -r 1 1 files updated, 0 files merged, 1 files removed, 0 files unresolved $ show C a C a.ignored - a:a a.ignored:a.ignored $ cd ../ && rm -rf r2 # # 4.3.2 # $ hg clone -q r r2 -u 2 && cd r2 # $ echo important-stuff > a/bla.ignored # $ hg up -r 1 # abort: Directory not empty: '$TESTTMP/r2/a' # [255] # $ show # ! a/something # I a/bla.ignored # C a.ignored # - # grep: a/something: No such file or directory # a/bla.ignored:important-stuff # a.ignored:a.ignored # [123] # $ cd ../ && rm -rf r2
Bisected for this particular case between 4.3 and 4.4, which says the first bad revision is: 2a774cae3a03 "merge: disable path conflict checking by default (issue5716)" $ hg clone -q r r2 -u 2 && cd r2 $ echo important-stuff > a/untracked $ hg up -r 1 2>/dev/null [255] $ cat a/untracked important-stuff Perhaps, some part of new path conflict handling is *enabled* without the safety check disabled by default.
Fixed by https://mercurial-scm.org/repo/hg/rev/8c6775e812d8 Yuya Nishihara <yuya@tcha.org> merge: do not delete untracked files silently (issue5962) 37450a122128, 2a774cae3a03, and 656ac240f392 weren't enough to prevent data loss. No unknown "files" weren't deleted before 7a8a16f8ea22, "context: also consider path conflicts when clearing unknown files." (please test the fix)
Thanks, the behavior seems to be consistent now (though I wished one could update past file/directory conflict).
> wished one could update past file/directory conflict Perhaps that can be achieved by experimental.merge.checkpathconflicts=1? Maybe we should move this option to [merge] and make it documented.
I tried the test above with experimental.merge.checkpathconflicts=1, and it doesn't seem to help (hg update still aborts in the last two cases, though with a different error), even with merge.checkignored=ignore.