Git merge with --squash while keeping log of every commit

我怕爱的太早我们不能终老 提交于 2019-12-22 10:12:53

问题


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.


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/33191293/git-merge-with-squash-while-keeping-log-of-every-commit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!