- # Consider this:
- # 1 --- 2 --- O --- X --- 3 --- 4 --- N
- #
- # O is $oldrev for $refname
- # N is $newrev for $refname
- # X is a revision pointed to by some other ref, for which we may
- # assume that an email has already been generated.
- # In this case we want to issue an email containing only revisions
- # 3, 4, and N. Given (almost) by
- #
- # git rev-list N ^O --not --all
- #
- # The reason for the "almost", is that the "--not --all" will take
- # precedence over the "N", and effectively will translate to
- #
- # git rev-list N ^O ^X ^N
- #
- # So, we need to build up the list more carefully. git rev-parse
- # will generate a list of revs that may be fed into git rev-list.
- # We can get it to make the "--not --all" part and then filter out
- # the "^N" with:
- #
- # git rev-parse --not --all | grep -v N
- #
- # Then, using the --stdin switch to git rev-list we have effectively
- # manufactured
- #
- # git rev-list N ^O ^X
- #
- # This leaves a problem when someone else updates the repository
- # while this script is running. Their new value of the ref we're
- # working on would be included in the "--not --all" output; and as
- # our $newrev would be an ancestor of that commit, it would exclude
- # all of our commits. What we really want is to exclude the current
- # value of $refname from the --not list, rather than N itself. So:
- #
- # git rev-parse --not --all | grep -v $(git rev-parse $refname)
- #
- # Get's us to something pretty safe (apart from the small time
- # between refname being read, and git rev-parse running - for that,
- # I give up)
- #
- #
- # Next problem, consider this:
- # * --- B --- * --- O ($oldrev)
- # \
- # * --- X --- * --- N ($newrev)
- #
- # That is to say, there is no guarantee that oldrev is a strict
- # subset of newrev (it would have required a --force, but that's
- # allowed). So, we can't simply say rev-list $oldrev..$newrev.
- # Instead we find the common base of the two revs and list from
- # there.
- #
- # As above, we need to take into account the presence of X; if
- # another branch is already in the repository and points at some of
- # the revisions that we are about to output - we don't want them.
- # The solution is as before: git rev-parse output filtered.
- #
- # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N
- #
- # Tags pushed into the repository generate nice shortlog emails that
- # summarise the commits between them and the previous tag. However,
- # those emails don't include the full commit messages that we output
- # for a branch update. Therefore we still want to output revisions
- # that have been output on a tag email.
- #
- # Luckily, git rev-parse includes just the tool. Instead of using
- # "--all" we use "--branches"; this has the added benefit that
- # "remotes/" will be ignored as well.
-
- # List all of the revisions that were removed by this update, in a
- # fast forward update, this list will be empty, because rev-list O
- # ^N is empty. For a non fast forward, O ^N is the list of removed
- # revisions
- fast_forward=""
- rev=""
- for rev in $(git rev-list $newrev..$oldrev)
- do
- revtype=$(git cat-file -t "$rev")
- echo " discards $rev ($revtype)"
- done
- if [ -z "$rev" ]; then
- fast_forward=1
- fi
-
- # List all the revisions from baserev to newrev in a kind of
- # "table-of-contents"; note this list can include revisions that
- # have already had notification emails and is present to show the
- # full detail of the change from rolling back the old revision to
- # the base revision and then forward to the new revision
- for rev in $(git rev-list $oldrev..$newrev)
- do
- revtype=$(git cat-file -t "$rev")
- echo " via $rev ($revtype)"
- done
-
- if [ "$fast_forward" ]; then
- echo " from $oldrev ($oldrev_type)"
- else
- # 1. Existing revisions were removed. In this case newrev
- # is a subset of oldrev - this is the reverse of a
- # fast-forward, a rewind
- # 2. New revisions were added on top of an old revision,
- # this is a rewind and addition.
-
- # (1) certainly happened, (2) possibly. When (2) hasn't
- # happened, we set a flag to indicate that no log printout
- # is required.
-
- echo ""
-
- # Find the common ancestor of the old and new revisions and
- # compare it with newrev
- baserev=$(git merge-base $oldrev $newrev)
- rewind_only=""
- if [ "$baserev" = "$newrev" ]; then
- echo "This update discarded existing revisions and left the branch pointing at"
- echo "a previous point in the repository history."
- echo ""
- echo " * -- * -- N ($newrev)"
- echo " \\"
- echo " O -- O -- O ($oldrev)"
- echo ""
- echo "The removed revisions are not necessarilly gone - if another reference"
- echo "still refers to them they will stay in the repository."
- rewind_only=1
- else
- echo "This update added new revisions after undoing existing revisions. That is"
- echo "to say, the old revision is not a strict subset of the new revision. This"
- echo "situation occurs when you --force push a change and generate a repository"
- echo "containing something like this:"
- echo ""
- echo " * -- * -- B -- O -- O -- O ($oldrev)"
- echo " \\"
- echo " N -- N -- N ($newrev)"
- echo ""
- echo "When this happens we assume that you've already had alert emails for all"
- echo "of the O revisions, and so we here report only the revisions in the N"
- echo "branch from the common base, B."
- fi
- fi
+ # Consider this:
+ # 1 --- 2 --- O --- X --- 3 --- 4 --- N
+ #
+ # O is $oldrev for $refname
+ # N is $newrev for $refname
+ # X is a revision pointed to by some other ref, for which we may
+ # assume that an email has already been generated.
+ # In this case we want to issue an email containing only revisions
+ # 3, 4, and N. Given (almost) by
+ #
+ # git rev-list N ^O --not --all
+ #
+ # The reason for the "almost", is that the "--not --all" will take
+ # precedence over the "N", and effectively will translate to
+ #
+ # git rev-list N ^O ^X ^N
+ #
+ # So, we need to build up the list more carefully. git rev-parse
+ # will generate a list of revs that may be fed into git rev-list.
+ # We can get it to make the "--not --all" part and then filter out
+ # the "^N" with:
+ #
+ # git rev-parse --not --all | grep -v N
+ #
+ # Then, using the --stdin switch to git rev-list we have effectively
+ # manufactured
+ #
+ # git rev-list N ^O ^X
+ #
+ # This leaves a problem when someone else updates the repository
+ # while this script is running. Their new value of the ref we're
+ # working on would be included in the "--not --all" output; and as
+ # our $newrev would be an ancestor of that commit, it would exclude
+ # all of our commits. What we really want is to exclude the current
+ # value of $refname from the --not list, rather than N itself. So:
+ #
+ # git rev-parse --not --all | grep -v $(git rev-parse $refname)
+ #
+ # Get's us to something pretty safe (apart from the small time
+ # between refname being read, and git rev-parse running - for that,
+ # I give up)
+ #
+ #
+ # Next problem, consider this:
+ # * --- B --- * --- O ($oldrev)
+ # \
+ # * --- X --- * --- N ($newrev)
+ #
+ # That is to say, there is no guarantee that oldrev is a strict
+ # subset of newrev (it would have required a --force, but that's
+ # allowed). So, we can't simply say rev-list $oldrev..$newrev.
+ # Instead we find the common base of the two revs and list from
+ # there.
+ #
+ # As above, we need to take into account the presence of X; if
+ # another branch is already in the repository and points at some of
+ # the revisions that we are about to output - we don't want them.
+ # The solution is as before: git rev-parse output filtered.
+ #
+ # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N
+ #
+ # Tags pushed into the repository generate nice shortlog emails that
+ # summarise the commits between them and the previous tag. However,
+ # those emails don't include the full commit messages that we output
+ # for a branch update. Therefore we still want to output revisions
+ # that have been output on a tag email.
+ #
+ # Luckily, git rev-parse includes just the tool. Instead of using
+ # "--all" we use "--branches"; this has the added benefit that
+ # "remotes/" will be ignored as well.
+
+ # List all of the revisions that were removed by this update, in a
+ # fast forward update, this list will be empty, because rev-list O
+ # ^N is empty. For a non fast forward, O ^N is the list of removed
+ # revisions
+ fast_forward=""
+ rev=""
+ for rev in $(git rev-list $newrev..$oldrev)
+ do
+ revtype=$(git cat-file -t "$rev")
+ echo " discards $rev ($revtype)"
+ done
+ if [ -z "$rev" ]; then
+ fast_forward=1
+ fi
+
+ # List all the revisions from baserev to newrev in a kind of
+ # "table-of-contents"; note this list can include revisions that
+ # have already had notification emails and is present to show the
+ # full detail of the change from rolling back the old revision to
+ # the base revision and then forward to the new revision
+ for rev in $(git rev-list $oldrev..$newrev)
+ do
+ revtype=$(git cat-file -t "$rev")
+ echo " via $rev ($revtype)"
+ done
+
+ if [ "$fast_forward" ]; then
+ echo " from $oldrev ($oldrev_type)"
+ else
+ # 1. Existing revisions were removed. In this case newrev
+ # is a subset of oldrev - this is the reverse of a
+ # fast-forward, a rewind
+ # 2. New revisions were added on top of an old revision,
+ # this is a rewind and addition.
+ # (1) certainly happened, (2) possibly. When (2) hasn't
+ # happened, we set a flag to indicate that no log printout
+ # is required.