Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I trivially linearize my git history?

Tags:

Question

Given that I have a commit like the following:

A - B - C - D - G - H      \         /       - E - F - 

How do I get a perfectly linear history following only the first parents?

Eg, I want to get:

A - B - C - D - G'- H' 

It is expected that the sha1's of G and H will change, as noted above. (For obvious reasons). If the sha1's for A, B, C, and D change as well, then I want to know why.

Background

The intention is to avoid potential merge commits that would arise when doing a naive git rebase -i.

The real point is to hopefully gain additional insight in order to determine which commits are in a particular branch, eg, if we have the following graph:

I - J - K - L - M  \     /   /   /    - N - O - P - 

Where N was merged into K, but O was merged with --strategy=ours into L, and P was merged into M.

I want to be able to linearlize my history so that such problematic commits can be identified, and inspected. I want to have a tree where can identify that O was not put into the upstream branch, even if I potentially identify N and P as being potentially missing from the upstream branch as well, by using git cherry, however any suggestions here would be appreciated.

like image 722
Arafangion Avatar asked Aug 01 '13 07:08

Arafangion


People also ask

What is linear git history?

A linear history is simply a Git history in which all commits come after one another. I.e. you will not find any merges of branches with independent commit histories.

Does git merge create a linear history?

Avoid [merge --no-ff] So GIT will create a linear history without creating a "new merge commit", just by mixing and move HEAD pointer. 👉 This case can change, it depends on: Is the target branch ahead (more changes) ? Gitlab/Github merges configuration (default --no-ff ? )

Why is git linear history important?

No extraneous commits in history. Splitting branches/PR's into smaller chunks for easier review, and testing is easier without merge commits. Being able to rebase feature branches easily (Fx. to change merge order) It encourages meaningful commit messages (but does not enforce it)


1 Answers

As you said, the following command works:

git filter-branch --parent-filter 'cut -f 2,3 -d " "' 

Why?

The problem you pose is solved by transforming each merge commit with a simple commit: this will simply remove the feature branches that were merged, since they will become orphan.

Each commit has one or more parent commits. Merge commit are the one which get more than one. Git stores this in each commit object of the history.

The git filter-branch command, with the --parent-filter option, allows to rewrite every commit's parent, passed to the filter as -p SHA1, repeated if there are more than one parent. Our filter cuts the parent and forces every commit to have a single parent.

For bonus, here's how to do it manually on a precise commit, by re-creating a new commit:

  • get the commit tree and the first parent

    tree=`git show -s --format=%T SHA1` parent=`git show -s --format=%P SHA1 | cut -d " " -f1` 
  • make a new commit with the same tree, same message, and keep only the first parent as ancestor

    git show -s --format=%B SHA1 | git commit-tree $tree -p $parent 
like image 57
CharlesB Avatar answered Oct 01 '22 12:10

CharlesB