Added
git hunk stash popmerges a stash back into files that have other unstaged changes, whichgit stash poprefuses ("Your local changes to the following files would be overwritten by merge"). That refusal met every stash of some of a file's hunks and not others, and every file edited again after stashing. The entry's worktree changes are merged file by file, the index is left as it is, and a conflict is left as git leaves one: markers labelledUpdated upstreamandStashed changes, unmerged index entries,CONFLICT (content): Merge conflict in <path>andThe stash entry is kept in case you need it again., exit 1. Where git's pop succeeds it still does the work, so the result is git's own. An untracked file in the way now refuses the pop before anything is restored, where git's pop merged the tracked changes first; and a conflict in git's own pop is now reported, where only the exit status said so before.
Fixed
-
An untracked symlink's diff, which git-hunk writes itself, named the path bare where git would C-quote it (a TAB, a quote, a backslash or a control character, and under
core.quotePatha non-ASCII byte), and left off the TAB git ends a name containing a space with. A symlink with a TAB in its name could not be added (bad git-diff - inconsistent new filename), anddiffshowed a header git never writes. The header is now git's own, byte for byte. -
Adding a file's deletion together with the untracked file it was moved to stages a rename, but
addreported the deletion as→ ?, a hashlist --stagednever shows. Both are now reported on one line under the rename's hash:staged 7c745cd aa914db → b34f350 new-name.txt. -
A git failure while
stashwas writing the stash tree or the untracked-files commit, or whilerestore --3waywas recording a conflict, exited on the spot and left agit-hunk-*temp index behind in the temp directory. Those failures now unwind, so the temp index is removed; the message and exit status are unchanged. -
On an unborn branch (no commits yet),
commitfailed with git'sfatal: Not a valid object name HEAD, andstashwith a rawgit rev-parsefailure.commitnow makes the first commit, asgit commitdoes;commit --amendsayserror: you have nothing to amend, andstashsayserror: you do not have the initial commit yet, in git's words, changing nothing.list --staged,count --stagedandresetalready worked there, against the empty tree, asgit diff --cachedandgit resetdo. -
A typechange between a binary and a symlink (a binary replaced by a symlink, or the reverse) could not be added, reset, committed, stashed or restored:
error: <path>: already exists in index(orin working directory). The binary half is staged, unstaged or restored by path, which takes the whole path, and the symlink half was applied as a patch beside it, before the binary half had made room or after it was already done. When both halves are selected, the symlink half now goes with the binary half. -
stashof a hunk in an intent-to-add entry (git add -N), or in a rename whose new side is one, took the file out of the worktree and left the entry naming it, so it showed as deleted.stashnow refuses while the index holds any intent-to-add entry, asgit stashdoes, and nothing changes:error: cannot stash while '<path>' is intent-to-add, exit 1. That includes stashing other files: a stash made beside such an entry cannot be popped while the entry stands. -
A line spec on a new or deleted file failed in most directions. The patch header was fixed when the diff was read, so the filtered patch still claimed to create or delete the whole file while its body kept the deselected lines, and git refused it:
reset <sha>:2on a staged new file,restore --force <sha>:2on an untracked file, andadd/commitof part of a deletion all failed ("new file depends on old contents", "patch did not apply cleanly"). Headers are now rendered from what the line spec leaves: the deselected lines stay where they were, so a partial undo of a new file or a partial deletion is an edit to a file that still exists. Theindexline is kept, so--3wayworks on a partial selection too. -
A line spec on an empty file (
add <empty>:1) silently applied the whole file. It is now rejected, as are line specs on a symlink (its one line is its whole target) and on either half of a typechange:error: line selection not supported for empty file|symlink|typechange '<path>', matching the existing message for binary files. -
Every change to a binary path had the same hash (
bin.dathashed alike as a worktree edit and as a new file under--ref), so a hash listed before the file changed again still matched it andcheckcould not notice. A binary hash now includes the blob ids from itsindexline. Binary hashes change with this release, and differ between SHA-1 and SHA-256 repositories. -
An empty new or deleted file's patch carried
--- /dev/null/+++ b/<path>lines git never writes for one, with the path unquoted even where git would quote it. The header is now git's own:diffshows it that way, andgit applytakes it as is. -
An untracked binary file's hash depended on
core.abbrev: untracked files were diffed without--full-index, so the blob ids its hash is taken over came abbreviated. They are now full, as for tracked files, and an untracked file's patch (a symlink's too) carries full ids in itsindexline. -
A file named like a revision broke the commands that name one: with a file called
main,list --staged --ref mainfailed with git's "ambiguous argument 'main': both revision and filename", and a file calledHEADmade everystashfail the same way. Revisions now always end with--, so git never has to guess. -
The result hash
addandresetprint for a rename was not onelistwould show: adding a rename reported it as a new file under a hash thatlist --stagednever lists, and resetting one printed→ ?, as did resetting a staged new file. The result is now read back from a diff that covers both paths of a rename and, forreset, the untracked file it leaves: adding a rename reports the rename'slist --stagedhash, and resetting one reports the new path, now untracked, and the old path's deletion (unstaged b34f350 → 7c745cd,aa914db new-name.txt). -
A
stashentry recorded the stashed hunks on top of HEAD as both its tree and its index commit, so any staged change madegit stash pop --indexfail ("Index was not unstashed") and left the staged file unstaged; with nothing staged,pop --indexstaged the stashed hunks. An entry now has the same shape asgit stash push --keep-index -- <paths>would give it: its index commit is the index as it stands and its tree is that index plus the stashed hunks. Every git stash command treats it as it would the native entry:pop --indexandapply --indexrestore the stashed hunks unstaged and keep the index, andgit stash showlists staged changes as well as stashed ones, as it does for a native keep-index entry. A stash while the index has unmerged paths is refused (error: cannot stash while the index has unmerged paths), asgit stashrefuses it, rather than recording an index commit that was not the index. -
--ref nopefailed with git's complaint about the empty tree it had been expanded against. Every revision--refnames, including each side of a range, is now checked before anything runs:error: bad revision 'nope'. -
A path containing a space kept the TAB git ends such a name with on its
---/+++lines, solist --file "a b.txt"found nothing,list --porcelainprinted an extra empty column,add/resetprinted→ ?, and a rename to or from such a path reported a result hashlistnever shows. The TAB is now dropped as git defines it: only after a name containing a space. Hashes of hunks in paths with spaces change with this release. -
Under
diff.renames=copies, a copied file'scopy from/copy tolines were dropped, so git applied the section as a rename:resetof the copy's hunk took the source out of the index along with its own staged change, andadd --refof a copy commit staged a rename. Copies now stay copies. Taking a copy's hunk back out (reset,restore,stash) edits the copy alone, leaving it a copy of its source, sincegit apply --reversewould otherwise write the copy back over the source. A copy with no content change is named by-vlike a rename with none. -
An untracked file whose name git C-quotes (
ünï.txt, or one with a quote or a tab in it) was invisible:listdid not show it, so it could not be added, and resetting a staged new file with such a name printed→ ?. Untracked files are now listed NUL-separated, as git names them. The listing is also scoped to the paths in question, soadd/resetno longer list every untracked file in the repository twice to report their results. -
apply.whitespace=errormadeadd,stash,commitand every other command that applies a patch reject a hunk with trailing whitespace, andapply.whitespace=fixwould have stripped it on the way. The content is already in the worktree or history; git-hunk now moves it as it is, asgit adddoes. -
A
stashwhose stashed changes could not be taken back out of the worktree printed a warning and exited 0, with the changes both stored and still in the worktree. It is now an error, exit 1:error: cannot remove the stashed changes from the worktree, with hints that the changes are saved instash@{0}and still in the worktree, and thatgit stash dropkeeps working on them. A tracked binary that could not be checked out, or an untracked file that could not be deleted, counts the same. -
restore --3wayhandedgit applya--3waythat implies--index. Without--refit always failed ("does not match index", since the hunk is exactly where worktree and index differ), and a partial restore of a deleted file wrote conflict markers and left unmerged entries; with--ref, a restore that applied cleanly was silently staged, and one into a file with other unstaged changes failed even where it applied without merging.restorenow changes only the worktree: a hunk that applies, directly or merged, leaves the index alone, so without--ref--3waychanges nothing (asgit restore --mergedoes on a path with nothing to merge). Only a merge that conflicts touches the index, recording the conflict asgit apply --3wayandgit stash applydo, with what was staged as "ours".
Changed
- A
stashentry's message is the onegit stash pushwrites:WIP on <branch>: <sha> <subject>, orOn <branch>: <msg>with-m, instead ofgit-hunk stash: <paths>or the bare-mtext. restoreof an untracked file without--forcenow saysrestoring it cannot be undone; use --forceinstead ofuse --force to delete: restoring part of an untracked file removes only those lines.- File sections are parsed into data that hunks share, and patch headers are rendered when the patch is built instead of when the diff is read. A filtered hunk's
@@line is written the way git writes it (a count of 1 is left out, and a side that becomes empty is numbered by the line before it). - Messages name the
--refyou typed rather than what it was expanded to or a mode it does not have:- Nothing to act on:
no changes in 'X'(or'A..B') andno staged changes relative to 'X', instead ofno unstaged changes.listandcountreport an empty ref diff as they do a clean tree: nothing, and0. - A
--refpatch that does not apply:error: changes from 'X' do not apply cleanly to the index (try --3way)(orto the worktreeforrestore), instead ofpatch did not apply cleanly — the diff from 'X^..X' may conflict with the current state. - A range with
--staged:error: --staged compares the index with one commit; 'A..B' is a range, instead of--staged cannot be used with a range ref (contains '..'). -vsuggestsgit add <path>for a path with no hunk only among unstaged changes; elsewhere the note stands alone.
- Nothing to act on:
- The docs describe
--ref Xas that commit's changes against its first parent (the empty tree for a root commit) rather than asX^..Xwithgit showsemantics, which differ for a merge. --stagedand--refnow choose a diff source once, while parsing, instead of a staged/unstaged mode plus a ref string that--ref Xrewrote toX^..X. Hashes, ranges and patches are unchanged.