I have the following alias stored in my ~/.gitconfig file
reset-master = reset $(git merge-base master $(git rev-parse --abbrev-ref HEAD))
but when I run git reset-master
it fails with
$ git reset-master
error: unknown option `abbrev-ref'
usage: git reset [--mixed | --soft | --hard | --merge | --keep] [-q]
[<commit>]
or: git reset [-q] [<tree-ish>] [--] <paths>...
or: git reset --patch [<tree-ish>] [--] [<paths>...]
-q, --quiet be quiet, only report errors
--mixed reset HEAD and index
--soft reset only HEAD
--hard reset HEAD, index and working tree
--merge reset HEAD, index and working tree
--keep reset HEAD but keep local changes
--recurse-submodules[=<reset>]
control recursive updating of submodules
-p, --patch select hunks interactively
-N, --intent-to-add record only the fact that removed paths will be added later
And running
git reset $(git merge-base master $(git rev-parse --abbrev-ref HEAD))
works perfectly fine. What am I doing wrong?
You need to use the !
form of a Git alias: !git reset $(...)
, rather than the shorter form, !reset $(...)
.
To understand the issue, we need to know that:
cmd1 $(cmd2 arg ...)
is actually handled by the shell , by running cmd2
with its arguments first . The output—more precisely, the standard output stream —from cmd2
goes back into the shell, which reads it and breaks it up into words, then passes those words to cmd1
. Hence:
wc $(seq -f f%g 1 3)
first runs seq -ff%g 1 3
:
$ seq -f f%g 1 3
f1
f2
f3
The shell reads these words, turns them into three arguments to pass to wc
:
wc f1 f2 f3
(This example is a bit silly since we know that seq -ff%g 1 3
will always print those three names, so we could just run wc
with those three names, but works for illustration.)
Git's aliases, by default, are not run through the shell:
reset-master = reset $(git merge-base master $(git rev-parse --abbrev-ref HEAD))
means that Git tries to pass the literal string $(git
as the first argument to git reset
. The second argument is merge-base
, and so on. One of the arguments is --abbrev-ref
, and that's the one git reset
looks at first—it's scanning through all the arguments for options, before it tries to make sense of the rest of the line—so that's the one it complains about.
If we feed the entire string to a shell , however, we get the shell to expand each of the $(...)
occurrences. The first one runs git rev-parse --abbrev-ref HEAD
, which prints the name of the current branch (if any, or HEAD
if we have a detached HEAD). This output gets fed back into the outer git merge-base
command, to find a merge base commit between master
and the named branch (or again HEAD
). With any luck, git merge-base
prints one commit hash ID, which the shell substitutes in for the final command:
git reset <hash-id>
We can get Git to do this by writing:
reset-master = !git reset $(git merge-base master $(git rev-parse --abbrev-ref HEAD))
as a Git alias that starts with !
means run the remaining text through the shell .
Note that if there is no merge base commit, this will run git reset
, which is equivalent to git reset --mixed HEAD
, which will re-set the index to match the HEAD
commit. If there are multiple merge bases—this is rare but not impossible—Git will pick one at what looks like random. It might be nice to write a shell function, or a shell script, that uses git merge-base --all
to verify that there is exactly one merge base, but the simpler alias will work for almost all real cases.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.