Git merge with --squash while keeping log of every commit
Asked Answered
O

3

7

Initial scenario:

A (master)
 \
 B - C  - D (development)

What I want after merge --squash:

A     -     E (master/development)
 \         /
 B - C - D 

On branch master, git log would be

commit E
    Squashed commit of the following:
    commit D
    commit C
    commit B
commit A

Keep developing on branch development:

A     -     E (master)
 \         / \         
 B - C - D    F - G - H (development)

Merge with squash again:

A     -     E     -     I(master/development)
 \         / \         /
 B - C - D    F - G - H

On branch master, git log would be

commit I
    Squashed commit of the following:
    commit H
    commit G
    commit F
commit E
    Squashed commit of the following:
    commit D
    commit C
    commit B
commit A

On branch development, git log would be

Commit I
Commit H
Commit G
Commit F
Commit E
Commit D
Commit C
Commit B
Commit A

I would like to merge with commits squashed on master while keeping every commit on development.

I have no idea how to implement that. My problem is that I don't know how to make D point to E in the first merge, and I will include B,C,D,E,F,G,H instead of F,G,H only.

Oxalis answered 17/10, 2015 at 20:30 Comment(1)
Why do you want to do this? It seems to make history more complicated. Also your log results will not be as you think. master's log will show all commits.Eanore
E
5

You can't get the log result you want with the structure you showed (git log --merges master will sort of do it). The structure that will give you the log you want defeats the point. And, in the end, this is a self-defeating way to work with Git.


The desire is to be able to run git log master and only see the squashed commits. This won't work. Git doesn't know that some commits are intended for master and some are for development. Let's look at the proposed structure.

A     -     E     -     I [master] [development]
 \         / \         /
 B - C - D    F - G - H

At this point, master and development point to the same commit. As far as Git history is concerned they are identical. A branch is nothing more than a label pointing at a commit. Commits don't remember what branch they were committed to. git log master and git log development will produce the same logs showing all commits from A to I. The E and I squashed commit logs will be redundant.

You can get what you want with git log --merges master (or development) to show only merge commits, but that will show any merge commits, even those done as part of development. So it doesn't really work.

This whole idea is unnecessarily complicated, read on.


To get the log result you want, you'd have break the relationship between the two branches like this.

A     -     E     -     I [master]
 \                   
 B - C - D - F - G - H [development]

Which you can make work somehow, but there's no point. git log master contains all the same log messages so it will be just as long as git log development, but they'll be smashed together. You can't use git log master to do code archaeology (ie. "why was this line written this way") because the changes are all smashed together into one diff making it harder to associate a line of change with a particular commit message. Since the history of master and development is disassociated, there's no way to ensure everything in development made it into master, or vice versa (for example, hot fixes to master).

git log master provides less information than git log development and it's harder to understand. master has no association with development and loses all the benefits of keeping the merge history. There's no point in maintaining this complicated setup.


Instead, use git merge --no-ff to merge feature branches (not one continuous "development" branch) and keep the branch history for easy archaeology.

              G - J [feature/tacos]
             /
A     -     E     -     K [master]
 \         / \         /
 B - C - D    F - H - I

E and K are normal merge commits produce by git merge --no-ff. There is no continuous development branch, that is handled by feature branches. Feature branches are single use and deleted once merged. The information about feature branches is preserved in the merge commit and git merge --no-ff guarantees the branch structure is preserved. feature/tacos is a feature branch to work on tacos that has not yet been merged.

git log --graph --decorate master will show the complete history of master, with the merge commits showing when features ended, plus lines illustrating the branch history. A GUI Git history tool such as GitX is another way to read the history as a graph.

In the end, Git history is a graph. If you learn how to work with that graph life with Git will be much easier. If you try to make Git history linear, like a big stack of pancakes, you're defeating the point of Git and creating extra work for yourself and everyone else.

Eanore answered 17/10, 2015 at 21:53 Comment(0)
W
1

Well, it is impossible to get exactly what you are asking for, as if you merge branches, you’ll see commits from both branches in your commit log anyway. What you might want is something like that:

A      -     E (master)
 \         
 B - C - D (development)

You just cherry-pick the commits from the development branch, squash them and apply on top of master.

The easiest way to get this is something like that:

git checkout development && git rebase -i master HEAD

Then you replace all the actions, except for the first one, with squash. As a result you’ll get a detached head that points to the squashed commit, applied on top of master. You’ll just have to reset master to this commit and you are done.

I guess, the commit message will be not exacly what you wanted, as it will resord commit messages of all the commits, not their hashes, but you’ll be able to hook into the commit message generation with the --exec option.

Everything is a little bit too manual, but you can script everything and create an alias. I believe, that’s the closest you can get using git itself, and not rewriting everything in some higher-level programming language.

Wivestad answered 17/10, 2015 at 22:15 Comment(0)
A
0

That is just a regular merge...

#!/bin/sh
git init
touch a; git add a; git commit -m a
git checkout -b patch
touch b; git add b; git commit -m b
touch c; git add c; git commit -m c
git checkout master
git merge --no-ff patch
git log --graph --oneline

Result

*   a9fbaec Merge branch 'patch'
|\
| * fb7eac4 c
| * 94f44c1 b
|/
* 4de57a5 a
Agape answered 17/10, 2015 at 21:3 Comment(2)
But you the commits aren't squashed in branch master git logOxalis
thanks for pointing it out, I have edited my question, hope it would be clearerOxalis

© 2022 - 2024 — McMap. All rights reserved.