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.