0

When I merge code and there are conflicts, I have a quick command that lists all the files in the "modified" block of git status. It's a hack using grep and git status.

function gitfix() {
   echo 'git status | grep modified:' 
}

It's nice because I just do

emacs -nw $(gitfix)

and my editor loads up all the files that need conflicts resolved.

But I want a more proper version that uses git ls-files.

The git ls-files command has statuses like "modified" and "unmerged" and I'm not sure what those mean as regards merge conflicts.

Yeah, I know I'm lazy (I'm a programmer) for not figuring this out myself but intentionally creating conflicts is a pain in the ass if I'm the only committer. Anyone using git ls-files to list the files that have conflicts during a merge?

Pᴇʜ
  • 56,719
  • 10
  • 49
  • 73
Walt Howard
  • 7,676
  • 1
  • 16
  • 11
  • To resolve conflicts I use alias [`edit-unmerged`](https://github.com/GitAlias/gitalias/blob/e5d366c356a15ad91bd84344af12b15fbccf9bb9/gitalias.txt#L1215) and then [`add-unmerged`](https://github.com/GitAlias/gitalias/blob/e5d366c356a15ad91bd84344af12b15fbccf9bb9/gitalias.txt#L1205). So I primarily use `ls-files --unmerged`. Full disclosure: I am a [contributor](https://github.com/GitAlias/gitalias/commits?author=phdru) to the repo. – phd Jan 03 '22 at 12:34
  • @phd ooo, I didn't even know about this repo, cool-o-rama! – matt Jan 03 '22 at 18:14
  • @matt I mentioned the repo in comments many times. :-) Once even in an [answer](https://stackoverflow.com/a/44548013/7976758). :-))) – phd Jan 03 '22 at 18:41

1 Answers1

1

phd's comment referring to this line:

edit-unmerged = !"f() { git ls-files --unmerged | cut -f2 | sort -u ; }; `git var GIT_EDITOR` `f`"

is a direct answer to what you want to do, but it's worth pointing out that git status does a lot more than git ls-files can do. Git is built around a "tool philosophy" and some tools are lower level and others are higher level; git status is relatively high level and incorporates things that have to be done via multiple invocations of multiple lower-level tools:

  • It will count how many commits the current branch is ahead and/or behind its upstream, if there is a current branch and it has an upstream. This requires using git symbolic-ref or similar to find the current branch, git rev-parse or similar to find its upstream, and git rev-list --count --left-right to do the counting.

  • It will check to see if some files are unmerged, and if so, change its strategy. The git ls-files command can do this but does not change strategy as a result: it takes one invocation of git ls-files to discover this, and then one or more separate invocations later depending on the strategy.

  • It will compare HEAD (the current commit) vs the index: git ls-files can do this, but it takes multiple invocations of git ls-files.

  • It will compare the index vs the staging area: git ls-files can do this too, but again it takes multiple invocations.

The main thing to know here is that git ls-files is oriented towards examining Git's index. The index itself is a complicated beastie, but its main contents are a list of <file name, staging number, hash ID, cache data> tuples:

  • the file name is the name of the file as it will appear in the next commit, if you make one now;
  • the staging number is a numeric value between 0 and 3 inclusive;
  • the hash ID is the internal blob hash for the file, or all-zero for an intent-to-add entry;
  • the cache data include lstat data and flags like assume-unchanged and skip-worktree: stuff intended to make Git go fast and/or be more useful.

If all of these entries have staging number zero, there are no unmerged files. Any entry with a nonzero staging number represents an unmerged file, and the staging number tells us its source:

  • 1 = the file as it appears in the merge base;
  • 2 = the file as it appears in --ours, i.e., the HEAD commit;
  • 3 = the file as it appears in --theirs, i.e., the other commit.

The name of the file in each of these up-to-three nonzero stage entries is normally the same for all three, but when a file was renamed, Git's index entries get a little squirrelly here. (There might, in theory, be more than three entries, though with just three stage numbers: Junio Hamano has mentioned on the mailing list that the intent was to allow multiple stage-1 entries during complex multiple-merge-base and/or octopus merges, for instance. In practice that doesn't happen, though. I think there are some additional issues here with the current index format that require either extensions or a new index format. But they're all extremely rare in practice anyway.) In any case, the presence of any nonzero stage numbers means that the index cannot be written out to a tree: git write-tree will fail. Some Git scripts use this as a short-cut test for "do unmerged index entries exist":

unmerged=false
t=$(git write-tree 2>/dev/null) || unmerged=true
if $unmerged; then ...; fi # test for unmerged entries

though technically git write-tree could fail for other reasons (.git/index being unwritable due to full file system, over-quota status, and/or permissions issues for instance).

In any case, if all index entries are at stage zero, there are no unmerged files, and the index can be turned into a new commit. The file contents in this proposed commit may match those in the HEAD commit, or not; they may match those in the working tree, or not; the index's set of files is independent of both of those.

torek
  • 448,244
  • 59
  • 642
  • 775