Skip to content

Quote untracked filenames in the merge-failure cleanup commands #13

Description

@chris-peterson

Context

When an integration merge fails, git fi resets the working tree and then lists any files the failed merge left behind, offering a command line per file to clean them up:

Some extra untracked files have been left as a result of the failed merge(s):

 * conflict-file.txt

You can delete these by running:
  rm "conflict-file.txt"

That line is built by interpolating the filename into a double-quoted string (src/merge.ts, in mergeProcess's failure branch). Double quotes stop word-splitting and globbing, but they do not stop command substitution: $(...) and backticks are still expanded by the shell inside them.

The filenames are not the user's own. They are content carried in by a teammate's branch that the octopus merge started to apply before it failed, so their names are chosen by whoever wrote that branch. git fi then prints them, on a report whose whole purpose is to be read and acted on, to every person on the team whose next merge fails.

The output is also wrong in a second, quieter way. git ls-files --other --exclude-standard honors core.quotePath, which is on by default, so a filename with a non-ASCII byte comes back already C-quoted — and is then wrapped in a second pair of double quotes, producing a command that names no file that exists.

C-quoting? git's way of making an unusual path safe to print on one line: it wraps the path in double quotes and escapes the awkward bytes (caf\303\251.txt). Printable ASCII like $ and ` is not awkward by that definition, so it passes through untouched — which is why the quoting that fires here is the one case that doesn't help.

Three filenames, and what each currently produces:

Filename in the repo Printed command Result when pasted
a`id`b.txt rm "a`id`b.txt" runs id, then deletes the wrong path
c$(id)d.txt rm "c$(id)d.txt" same, via $( )
weird ünï.txt rm ""weird \303\274n\303\257.txt"" deletes nothing — the path doesn't exist

Proposed approach

Two changes, both mirroring what the conflict-attribution report already does for branch names:

  • Read the paths as NUL-terminated records. Add -z to both git ls-files --other --exclude-standard calls in mergeProcess — the before-snapshot and the after-snapshot — and split on NUL rather than newline. -z turns off C-quoting, so the path arrives as its real bytes, and a filename containing a newline stops being able to split one record into two. Both call sites need it together, since the two sets are compared to find what the merge added.

  • Single-quote the filename in the printed command. Single quotes are the only form that stops command substitution. git fi already has this in shq(), which quotes a branch name for exactly the same reason before printing it in a rebase command; the same function applies unchanged to a path.

shq() currently lives as a module-private helper in src/readiness.ts. Wherever it ends up, one implementation should serve both call sites — the two are the same problem, and a report that quotes the branch name on one line and not the filename three lines below is the state this issue is about.

Acceptance criteria

  • A file whose name contains a backtick or $( ), left behind by a failed merge, is printed in a rm line that deletes exactly that file and executes nothing else when pasted into bash or zsh.
  • A file whose name contains a space or a non-ASCII character is printed in a rm line that names the real path, with no leftover C-quote escapes and no doubled quotes.
  • A file whose name contains a newline does not split into two entries in either the bullet list or the rm lines.
  • A file with an ordinary name still prints as rm conflict-file.txt or rm "conflict-file.txt" — something a reader recognizes as what they would have typed.
  • Tests cover the backtick, the non-ASCII, and the ordinary case.
  • MERGE-11 in SPEC.md states the quoting rule for the paths it specifies printing.

Considerations

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions