guettli
guettli

Reputation: 27969

git: diff between current branch and branch creation

Imagine this:

git-diff-three-dots

I created the branch "B" three days ago. It is the branch I am currently working on. Now I want to know what changed since the branch was created (X).

This gives my a diff between B and X (like the dashed line in the picture):

git diff A...

Above command is handy, since it is short and I don't need to remember "X".

But: I am super lazy. I don't want to remember which branch my current branch was created from.

How can I avoid to remember/type A?

I am looking for a way which needs no input from my side. It should list all changes since the last time a branch was created.

Upvotes: 10

Views: 1714

Answers (6)

LeGEC
LeGEC

Reputation: 51988

I will repeat what @matt has amply detailed in his answer : git tracks commits, but not branches


There is however something that looks like "the life of that branch", it's the reflog for that branch :

git reflog my/branch

You can inspect the history and see if you find any information there.

For example : the commit when the branch was created would be :

git reflog my/branch | tail -1
  # and use something like `| awk '{ print $1 }'` for the sha,
  # or parse the 'branch: Created from ...' message (if there is one)
  # to see if there is a branch name

git reflog --format="%H" my/branch | tail -1   # if you want only the sha

But again, there are caveats : if you have run git rebase during the lifetime of the branch, then the initial commit is probably not the fork point you are looking for anymore.


Another proxy could be : look at the commits that are in the history of branch B, and in the history of no other branch :

# the following will list all branches except 'my/branch' :
git branch --format="%(refname:short)" | grep -v "my/branch"

# here is a way to list all commits on 'my/branch', excluding commits
# from all other branches.
# Adding `--boundary` will also display the first 'hidden' commit
git log --boundary --oneline --graph my/branch --not $(<the command above>)

# you can also replace 'git log' with 'git rev-list', if you only want the hashes

With some luck, the commit mentioned as a boundary is the one you are looking for.
The caveats being : if there are some merges in the history of your branch, you may have several "boundary" points, and if there is another branch that "forked off" from B more recently than the X you are looking for, then the boundary would be the fork point with that branch, and not with master or develop.


update based on your comment : you can

  • list all feature branches in your local clone (except your own branch)
  • look for the "fork point" of your current branch from any of those branches
# get the name of your current active branch :
mybranch=$(git rev-parse --abbrev-ref HEAD)

# make a separate function to list on stdout "all the branches except mine"
list_branches () {
  # list the `master` branch
  echo "master"

  # get all 'origin/somefeature' branches, excluding mybranch

  # suggestion: list remote branches (you will get all features,
  # even if you haven't created a local checkout of a branch)
  git branch -r --format="%(refname:short)" |\
    grep "/feat/" |\    # you can grep for a pattern if you have one
    grep -v "$mybranch" # remove your branch from the lot
}

# get the 'boundary' commits of your branch with respect to
# all the other branches
base=$(
  git rev-list --boundary --graph "$mybranch" --not $(list_branches "$mybranch") |\
    grep -e "^-" |\  # rev-list will prefix boundary commits with a '-'
    tail -1 |\       # if there are several, only take the last one
    tr -d '-'        # delete that leading '-'
)

# with some luck, we computed the correct base commit :
git diff $base...

Upvotes: 3

knittl
knittl

Reputation: 265547

It's not possible after the fact (and doing git diff A... is the best and shortest option). However, if you know beforehand that one branch is upstream of another, you can set up the "upstream configuration" when creating the branch with the -t/--track option:

git checkout --track --branch newbranch upstreambranch

To configure the upstream for an existing branch, use the git branch command with the -u/--set-upstream-to= option:

git branch --set-upstream-to=upstreambranch existingbranch

(in your example: newbranch/existingbranch == C, upstreambranch = A)

You can then access the current branch tip commit of the upstream branch with newbranch@{upstream} or newbranch@{u} for short. To get the upstream of the current branch (HEAD), @{u} suffices.

Your command then becomes:

git diff newbranch@{u}...newbranch

or, with newbranch checked out:

git diff @{u}...

Upvotes: 0

jthill
jthill

Reputation: 60393

I don't want to remember which branch my current branch was created from. How can I avoid to remember/type A?

Tell Git to track it for you. To make this happen, when you want to branch off your current checkout, add the t option:

git checkout -tb B

will switch to a new branch B tracking your existing ... whatever branch, A, right? Notice I didn't have to remember or type it. See the git branch -u option for setting a new upstream after the fact.

When you've told git what branch you're tracking, git diff @{u}... or git diff @{upstream}... does exactly what you want. You could even make it an alias, git config --global alias.du diff @{u}....


Note that Git ordinarily sets up tracking on upstream branches from remotes automatically: if you say git checkout fix32943 and you don't have a fix32943 branch and there's exactly one remote with a fix32943 branch, Git will make a local fix32943 branch and set it up, as if you'd done the above because that's exactly what it's doing for you, to track that upstream. So git diff @{U}... often already works the way you want it to.

Upvotes: 3

codeLvr
codeLvr

Reputation: 3

As everyone before me already said, git tracks commits and not branches. You could tag a commit before you branch from it to make it easier, but that still requires you to manually do the tagging.

Steps:

  • git tag <some descriptive tag>
  • git push origin <tag name> if single tag
    • git push --tags for multiple tags

then from here you can easily look at logs and see changes as needed.

Or you could just name your branch branchName_branchedFrom type of naming convention and then it'll be easier to track (where branchedFrom could be the short-sha1 or if you've tagged it, could be the tag name.

Upvotes: 0

VonC
VonC

Reputation: 1326782

But: I am super lazy. I don't want to remember which branch my current branch was created from.

Then you need to script it, in an executable called git-diffca, which can then be called as git diffca (ca for "common ancestor")

In that script, you need to decide what criteria you want to chose amongst all the candidate branches:

     c--c--c     (C)
    /
a--Y--X--a--a    (A)
       \
        b--b--b  (B, HEAD)
     

What do you want to look at: changes from Y (a common ancestor between branch C and B)? or from X (between A and B)

I would look at all branches, compute git merge-base <aBranch> B for each one, and take the most recent commit (X in the case of the schema)

Then the script would display git diff A... (since git merge-base A B yielded the most recent common ancestor with B)

Upvotes: 5

matt
matt

Reputation: 535566

Well, @{-1} might be A. But then again it might not. It simply means "the branch I was on previously". Similarly, looking through the git reflog might tell allow you to deduce where you were when you created the current branch.

But there is nothing about the current branch itself that tells you any of that. The real problem here is that you have a different idea of what a "branch" is than Git does. The phrase "since the branch was created" is probably misleading you.

You seem to think that B is "everything since X, up to the end of B." It isn't. B is just one commit: the one labelled B in your diagram. Everything else backwards from that — the commit before B, and the commit before that, and X, and the commit before X, and the commit before that, backwards all the way to the root commit — have exactly the same status. They are commits reachable from B, and that is all they are.

So there is nothing special about X in Git's mind. You think it is special because it is where A and B "meet". But to distinguish that fact you must know the names of B and A. You are seeing a topology that depends upon A; you must communicate what you see to Git if you want Git to help you.

Once you are willing to talk about both A and B, then fine, you can ask for git diff ...A and git diff A... to find out what changed since X. Or you can talk about git merge-base A B to find X. But only a human being can distinguish that the key here is A.

Upvotes: 10

Related Questions