Let's say I have the following history
D---E-------F
/ \ \
B---C---G---H---I---J
/ \
A-------K---------------L--M
git log --ancestry-path D..M will give me
E-------F
\ \
G---H---I---J
\
L--M
However, I would like just the following
E
\
G---H---I---J
\
L--M
Or
E-------F
\
I---J
\
L--M
Essentially, I would like to traverse down only one path, not two.
Is this possible? And if so, what is the command?
Edit:
I've tried using --first-parent, but this isn't exactly it. git log --first-parent G..M gives me
F
\
H---I---J
\
L--M
It includes F, because F is the first parent of I. Instead I'd like
H---I---J
\
L--M
Any help would be appreciated
Solution (that worked for me):
As @VonC stated, there isn't a single one-liner that does this. So I ended up using a bash script.
For example, git log --first-commit G..M is
H - F - I - J - L - M
However, F's parent is E, not H. So we omit F, giving me
H - I - J - L - M
Yay!
There are many ways to rewrite history with git. Use git commit --amend to change your latest log message. Use git commit --amend to make modifications to the most recent commit. Use git rebase to combine commits and modify history of a branch.
Git file History provides information about the commit history associated with a file. To use it: Go to your project's Repository > Files. In the upper right corner, select History.
Steps to get to a clean commit history:understand rebase and replace pulling remote changes with rebase to remove merge commits on your working branch. use fast-forward or squash merging option when adding your changes to the target branch. use atomic commits — learn how to amend, squash or restructure your commits.
Git Log Oneline The oneline option is used to display the output as one commit per line. It also shows the output in brief like the first seven characters of the commit SHA and the commit message.
I don't think this is directly possible (unless you know in advance the exact list to include/exclude, which negates the purpose of walking the DAG)
Actually, the OP Ken Hirakawa managed to get the expected linear history by:
git log --pretty=format:"%h%n" --ancestry-path --reverse $prev_commit..$end_commit
And for each commit, making sure it is a direct child of the previous commit.
Here is the script writtten by Ken Hirakawa.
Here is my script to create the DAG mentioned in the History Simplification section of the git log man page, for --ancestry-path
:
You will find at the end the bash script I used to create a similar history (call it with the name of the root dir, and your username).
I define:
$ git config --global alias.lgg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
I get:
$ git lgg
* d7c4459 - (HEAD, M, fromA) M <VonC>
* 82b011d - (L) Merge commit 'J' into fromA <VonC>
|\
| * 190265b - (J, master) J <VonC>
| * ef8e325 - (I) Merge commit 'F' <VonC>
| |\
| | * 4b6d976 - (F, fromB) F <VonC>
| * | 45a5d4d - (H) H <VonC>
| * | 834b239 - (G) Merge commit 'E' <VonC>
| |\ \
| | |/
| | * f8e9272 - (E) E <VonC>
| | * 96b5538 - (D) D <VonC>
| * | 49eff7f - (C) C <VonC>
| |/
| * 02c3ef4 - (B) B <VonC>
* | c0d9e1e - (K) K <VonC>
|/
* 6530d79 - (A) A <VonC>
From there, I cannot exclude one of the parents of commit I.
The ancestry-path does return:
$ git lgg --ancestry-path D..M
* d7c4459 - (HEAD, M, fromA) M <VonC>
* 82b011d - (L) Merge commit 'J' into fromA <VonC>
* 190265b - (J, master) J <VonC>
* ef8e325 - (I) Merge commit 'F' <VonC>
|\
| * 4b6d976 - (F, fromB) F <VonC>
* | 45a5d4d - (H) H <VonC>
* | 834b239 - (G) Merge commit 'E' <VonC>
|/
* f8e9272 - (E) E <VonC>
which is consistent with the log man page:
A regular
D..M
computes the set of commits that are ancestors ofM
, but excludes the ones that are ancestors ofD
.
This is useful to see what happened to the history leading toM
sinceD
, in the sense that "what doesM
have that did not exist inD
".
The result in this example would be all the commits, exceptA
andB
(andD
itself, of course).When we want to find out what commits in
M
are contaminated with the bug introduced byD
and need fixing, however, we might want to view only the subset ofD..M
that are actually descendants ofD
, i.e. excludingC
andK
.
This is exactly what the--ancestry-path
option does.
#!/bin/bash
function makeCommit() {
local letter=$1
if [[ `git tag -l $letter` == "" ]] ; then
echo $letter > $root/$letter
git add .
git commit -m "${letter}"
git tag -m "${letter}" $letter
else
echo "commit $letter already there"
fi
}
function makeMerge() {
local letter=$1
local from=$2
if [[ `git tag -l $letter` == "" ]] ; then
git merge $from
git tag -m "${letter}" $letter
else
echo "merge $letter already done"
fi
}
function makeBranch() {
local branch=$1
local from=$2
if [[ "$(git branch|grep $1)" == "" ]] ; then
git checkout -b $branch $from
else
echo "branch $branch already created"
git checkout $branch
fi
}
root=$1
user=$2
if [[ ! -e $root/.git ]] ; then
git init $root
fi
export GIT_WORK_TREE="./$root"
export GIT_DIR="./$root/.git"
git config --local user.name $2
makeCommit "A"
makeCommit "B"
makeCommit "C"
makeBranch "fromB" "B"
makeCommit "D"
makeCommit "E"
makeCommit "F"
git checkout master
makeMerge "G" "E"
makeCommit "H"
makeMerge "I" "F"
makeCommit "J"
makeBranch "fromA" "A"
makeCommit "K"
makeMerge "L" "J"
makeCommit "M"
I have to admit I didn't understand your solution - it didn't work for my example - but if I understood your use-case correctly (given a pair of commits, you want an arbitrary linear path between them, with no splits), I have the same problem, and the following solution seems to work:
A resulting script looks like:
#!/bin/bash
output_set=""; child_to_match=""; # init
while read -r; do
if { [ -n "$REPLY" ]; } && { [[ "${REPLY:41}" =~ "$child_to_match" ]] || [ -z "$child_to_match" ]; }; then
child_to_match=${REPLY:0:40}
output_set="$output_set $child_to_match"
fi
done <<< "$(git rev-list --ancestry-path --children $1)"
if [[ -n $output_set ]]; then
git show -s $output_set "${@:2}"
fi
It can be called like single-ancestry-path.sh RANGE_EXPRESSION DECORATION_ARGS
, supporting generally the same decoration arguments as git log
(it is in fact git show
, being called once per commit), so taking the famous lg2
example from https://stackoverflow.com/a/9074343/74296, the call might look like this: eg:
single-ancestry-path.sh master..MyBranch --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'
It's been 9 years, so I would have hoped there would be an easier answer, but I can't find one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With