1

When I type enter image description here in terminal, it works.

But whem I'm trying to aliasing this command , it occurs error: enter image description here

 Chois@Chois-MacPro book-example $git config --global alias.next 'checkout `git log --reverse --ancestry-path HEAD..master | head -n 1 | cut -d \  -f 2`'
 Chois@Chois-MacPro book-example $git next
error: unknown option `reverse'
usage: git checkout [<options>] <branch>
   or: git checkout [<options>] [<branch>] -- <file>...

    -q, --quiet           suppress progress reporting
    -b <branch>           create and checkout a new branch
    -B <branch>           create/reset and checkout a branch
    -l                    create reflog for new branch
    --detach              detach the HEAD at named commit
    -t, --track           set upstream info for new branch
    --orphan <new-branch>
                          new unparented branch
    -2, --ours            checkout our version for unmerged files
    -3, --theirs          checkout their version for unmerged files
    -f, --force           force checkout (throw away local modifications)
    -m, --merge           perform a 3-way merge with the new branch
    --overwrite-ignore    update ignored files (default)
    --conflict <style>    conflict style (merge or diff3)
    -p, --patch           select hunks interactively
    --ignore-skip-worktree-bits
                          do not limit pathspecs to sparse entries only
    --ignore-other-worktrees
                          do not check if another worktree is holding the given ref
    --progress            force progress reporting

Need your help.

Sorry for attaching image instead of real command but I can not express symbol ` itself.

user3595632
  • 5,380
  • 10
  • 55
  • 111
  • To include a backquote, use the indent-four-spaces-makes-a-code-block method (that's the easiest way anyway). – torek Jul 23 '16 at 10:16

2 Answers2

1

If a Git alias is not prefixed with an exclamation point !, Git tries to run the alias within itself, which requires that it not use any special shell features.

The shell features you are using are backquote expansion1 and piping (cmd1 | cmd2). Thus, your particular alias requires the ! form.

I see the idea behind the alias; you can simplify this alias a bit. But you might also want to make it a bit more robust, which will require a small shell script (it can be embedded into the alias, although that makes it harder to read).

What next does, and its slight defect (skip to the end if you already know)

The rev-spec HEAD..master denotes commits reachable from master that are not reachable from HEAD. For instance, given a rather complex graph:

       o--o--o--o        <-- brA
      /     /    \
...--o--o--o--o---o--o   <-- master
      \
       o--o--o           <-- brB
        \
         o--o--o         <-- brC

the sequence brA..master gets you this subset (taken commits *-ed, not-taken commits x-ed, boring unrelated commits left o):

       x--x--x--x        <-- brA
      /     /    \
...--x--x--x--*---*--*   <-- master
      \
       o--o--o           <-- brB
        \
         o--o--o         <-- brC

and brB..master gets you these:

       *--*--*--*        <-- brA
      /     /    \
...--x--*--*--*---*--*   <-- master
      \
       x--x--x           <-- brB
        \
         o--o--o         <-- brC

Using --ancestry-path trims the list of selected commits to just those that are descendents of the commit identified by the left-hand name (they must already be ancestors of the right hand name so this constraint has no effect). For the first case, brA..master, this drops one *-ed commit, which I will mark with ! here:

       x--x--x--x        <-- brA
      /     /    \
...--x--x--x--!---*--*   <-- master
      \
       o--o--o           <-- brB
        \
         o--o--o         <-- brC

and that, indeed, takes you to the "next" commit in the direction of master.

For the brB..master case, however, --ancestry-path removes all commits and the set becomes empty, because none of the * commits is a descendent of the tip of brB:

       !--!--!--!        <-- brA
      /     /    \
...--x--!--!--!---!--!   <-- master
      \
       x--x--x           <-- brB
        \
         o--o--o         <-- brC

Of course, rather than a branch name, we just use HEAD so that all this works with the detached-HEAD cases. But consider this graph as well, a sort of benzene ring of commits. I'll mark the current HEAD commit with H:

       o--o
      /    \
...--H      o--o   <-- master
      \    /
       o--o

In this case, HEAD is the left edge of the "benzene ring". Then HEAD..master selects all of the ring as well as the rightmost o:

       *--*
      /    \
...--H      *--*   <-- master
      \    /
       *--*

Meanwhile, --ancestry-path removes nothing at all: every *-ed commit is in fact a descendant of H. The next alias will choose one of these at semi-random—let's assume it chooses the top one—and move to it:

       H--o
      /    \
...--o      o--o   <-- master
      \    /
       o--o

and at this point next can never traverse the lower half of the ring, as those two commits are no longer descendants of HEAD.

In other words, the slight defect is that this assumes there are no internal branch-and-merge sequences between HEAD and the tip of master. If there are such sequences, and we wish to visit them, we must2 record the original --ancestry-path set of commit IDs once, up front, and then walk through them all (in whatever order we like, probably a reverse topological sort that allows us to jump back to the "lower half" of the benzene ring after traversing the "upper half", in our particular example).

Simplifying the alias

What git log ... | head -1 | cut ... does is produce the list of commit IDs and log messages, extract just the first line (which reads commit <id>, and extract just the ID from it.

We can do the same thing more simply with git rev-list. We still need the head -1 (or equivalent) because --no-walk or -n 1 gives us the commit ID of master itself, rather than the next commit:

git rev-list --reverse --ancestry-path ..master | head -1

(we get to omit the word HEAD too since that is the default).

If this produces no output, though, we probably should not run git checkout. Hence:

id=$(git rev-list --reverse --ancestry-path ..master | head -1)
test -n "$id" && git checkout "$id" || echo 'no more commits'

which is a two-line script. To embed this into an alias is a bit tricky because of all the quoting needed. Instead, I'd just make it a two-line shell script; but if you want it as an alias:

[alias]
  next = "!f() { id=$(git rev-list --reverse --ancestry-path ..master | head -1); \
  test -n \"$id\" && git checkout $id || echo 'no more commits'; }; f"

Both of these could take the name of the target, rather than hardcoding master. (But again I'd just dump all the commit IDs into a file and step through the file, with "next" and "prev" aliases, perhaps.)


1This is usually better written as $(command) instead of `command` since the parenthesized form nests and is easier to comprehend. Either way, it requires the shell, though.

2It's possible to just record the start and end points, and use a more complex algorithm to pick the next node based on where HEAD is now and our known traversal order. For instance, suppose we have the start and end points. We can generate the full list into a file, then git rev-parse HEAD and find where it is in the file, then step to the next one in the file and remove the file, which we won't need until we go to step forward again. But really, it's far easier to just dump the list into the file once, and then visit each entry one at a time without regenerating this file on each step.

torek
  • 448,244
  • 59
  • 642
  • 775
  • I would like to know how you find the time to regularly provide these extremely in-depth professional answers. I suspect you are a paid employee of SO or some git advocacy group because your responses are always even higher quality than those I see from contracted support service providers. You're a gem. – Jeff Puckett Jul 23 '16 at 15:47
  • @JeffPuckettII: Actually, I'm working on (or lately, trying to work on...) a book. So I have some of this somewhat canned, and am looking for the best way to convey the ideas completely yet compactly enough. Also, I find it easier (and faster) to write longer articles than shorter ones :-) – torek Jul 23 '16 at 20:51
  • Well please ping me when your book is available, or add jeffpuckett2@gmail to the promo distribution list. I think it will be one of my favorites. – Jeff Puckett Jul 23 '16 at 21:05
0

Try instead of using backquotes using the $() syntax (easier to parse)

git config --global alias.next 'checkout $(git log --reverse --ancestry-path HEAD..master |head -n 1 | cut -d \  -f 2)'
Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250