So, in many software you can do that easily using the mouse:
Although very useful, they aren't in chronological order anymore.
Is there anything wrong in doing that?
Commits don't have a chronological order. Commits have dependencies. Oh sure, we make them in chronological order. But they don't care. The fancy word here is topology, but lets keep it light.
In a sane world (certainly not this one) maintaining chronological order has the pleasant side effect of maintaining dependencies. When a need to change that order arises, you'd better know how to resolve these dependencies.
Commit C
might depend on commit A
but not commit B
. In such a situation reordering ABC
to ACB
maintains that dependency. But CAB
would break code.
Reordering commits is essentially rewriting code. Make sure you understand what you're writing.
Also, doing this counts as a rebase. Please don’t rewrite history publicly without telling me. Or one of us is in for a nightmare merge.
It depends.
This is a surprisingly deep question. At first glance, the answer by candied_orange is a bullseye and was my first thought as well. Reordering commits, as shown in the question, is a simple button click in the UI of a program. This belies the fact that reordering commits is a git rebase
under the hood. There is nothing wrong with reordering commits provided you are modifying your own private history. This follows good practices for rebasing commits in general, and is independent of whether you are squashing commits, reordering them, or simply incorporating new commits. This changes once you've pushed your branch.
Don't unless you coordinate with teammates.
After pushing your branch, other people can pull it down and continue working on it, rebase their branch onto yours, or merge it in. What used to be private history is now public. Any sort of rebase operation orphans commits you previously pushed, and this includes reordering commits.
Reordering commits will create new commit objects. The contents of the commits will be the same, but commit Ids will change, as will the parent commits. Git stores commits as a directed acyclic graph, so changing the order of commits modifies the graph tree of commit objects. Reordering commits you have already pushed will cause your teammates headaches if they have incorporated your changes into theirs because they no longer share history with your branch.
The flippant answer is it's your history, so just do it! Who cares if the application doesn't compile in one of the commits. It's your private history. Commit early and commit often. Break stuff as you see fit. Just please fix it before you push it. Rebasing and cleaning up your private history prior to pushing it is a good idea anyhow.
Linus Torvalds had some good advice regarding rebasing commits. The relevant bit being:
People can (and probably should) rebase their private trees (their own work). That's a cleanup. But never other peoples code. That's a "destroy history"
Source: Linus Torvalds; March 29, 2009; https://www.mail-archive.com/[email protected]/msg39091.html
This "cleanup" includes reordering commits if you deem it necessary. Now, go read candied_orange's answer about situations where code dependencies between commits can cause headaches. You could introduce code in a middle commit that later commits need to compile the application. Moving that middle commit later in the history could cause some of the commits in your branch to not work correctly. But if this is private history, who cares? Consider rebasing and squashing commits before you push, which will likely combine commits you had previously reordered, rendering this question moot.
Should you reorder commits you have already pushed? No. That destroys history and orphans your teammates' work.
Should you reorder commits you have not pushed? That's up to you. You could break the application, but then again, you are the only one to suffer, so you get to decide if this is "wrong". Just clean up that mess before you push.
I think there are different ideas of what a project's history should mean.
These views are somewhat in conflict with each other, and the choice of VCS systems has an impact too. Centralised VCS systems tend to be better at 2, while distributed version control systems, and particularly git, tend more towards 1.
Reordering commits in git is a form of "rebasing". Rebasing is usually done to replace real messy history, with a more logical set of steps taking us from the old state to the new state. It puts the focus of the history more on 3 and less on 1.
I think it would be pretty rare/weird to "just" reorder, I would expect reordering to be combined with cleaning up some commits, squashing others together and perhaps even dropping some entirely.
As well as the aesthetics of the history, it is also important to remember that git was originally designed to be "append only". Rebasing a branch effectively creates new commits, and in particular rebasing a branch that someone else has built work on top of can lead to a horrible mess. Once a commit has been pushed to a widely shared/collaborated on branch you should probably not rebase it.
Rebasing your private development branches that you haven't shared with anyone on the other hand is generally fine, and often encouraged. Having mistakes that never left a developers computer in everyones history isn't generally very helpful.
In the middle comes stuff like topic/feature/pull request branches. Rebasing such branches before merging them can make the history of the main branch a lot cleaner and easier to follow (particularly if the topic/feature branch contains a lot of "back-merges"), but it means that anyone who is following development on the topic/feature branch will have to take manual steps to reconcile their history.
From a more technical aspect on what could go wrong with reordering commit, someone wrote a nice explanation here: https://yuasakusa.github.io/git/2014/01/18/git-non-commutativity.html
In short, the content of the file after reordering might not be the same compared to without reordering.
The article I linked above provides the following example:
File content:
A
B
We then change it to the following and commit (first commit):
B
We then change it to the following and commit (second commit):
A
B
C
If we swap the order of the commits above, the file content progression becomes like this.
Initial:
A
B
After second commit applied:
A
B
C
After first commit applied:
B
C
So content after applying 2nd-1st commit is different from the content when doing 1st-2nd commit, and there is no conflict reported here. So after drag-and-dropping the commit via the GUI, for example, you might find that your file content is no longer the same.
git
records commits not as a sequence, but as a Directed Acyclic Graph, linking each commit to the parent commits its based upon. Obviously, that means that the parent commits must exist before the child commit can be created (enforced by the parent references being the cryptographic hash of the parent commit(s)). This cannot be changed without rewriting the history, recording the commits in the new sequence a second time. That is: None of the reordered commits is the same commit as it was before the reordering! They are a new tale of the history of the code, and may contain more errors than the actual history due to that fact.
Let's say, you are working on changing some internal algorithm to something else. As you work, you find that you need some new function within a different component. You write the tests for the new functions, you write the function, you test it, you commit it. Now you turn back to your main project, adding at least one call to the new function to your code. You commit the change. Now, what happens when you just easily reorder the commits using the mouse?
Well, the two commits did not even touch the same files. One added a function to component A, the other a call to component B. As such, your nice, slick tool performs the change without any hickup. However, with the commits reordered, the code won't even compile anymore! Because now you have added the function call before the function has been added to the project. Your compiler (hopefully) can't find the function, and aborts the build. You have created a broken lie about the history, without even noticing it. This can easily happen even when you didn't already publish the original commit order!
This is the only way that you can avoid falling into the trap outlined in part 2. When you choose to rewrite history, that's fine. But remember, it is your obligation to present a working history. You are creating new commits, you are required to test their code so that others can check out those commits and test the software for any regressions that they may be chasing. If you fail to check that those new commits actually work, you are making life unnecessarily hard for your future self and other developers who need to maintain your software in the future.
Unfortunately, there are a lot of people out there who are using git
without ever having truly grasped the reasons why git
does things the way it does them. Especially the point where git merge
should be used rather than git rebase
. And some of these people write tools to help them with their less-than-ideal workflows. Like the tool that allows you to "easily [reorder] commits using the mouse". And these tools are a sweet poison. Better develop a deep understanding of why multiparent commits have been invented, how git
is able to make sense of even the most tangled commit histories, helping you to record history as it happens, and to chase down regressions wherever they may be originated. Once you do understand these things, you won't let your tools entice you to unnecessarily rewrite history.
git bisect
to good use. The larger the gaps in buildability, the more involved the hunt for the actual cause of a recession.
Commented
Oct 26 at 20:55
git bisect --first-parent
comes in. It'll run only over the merge commits, which bisects at the PR level, rather than their constituent commits
Commented
Oct 26 at 21:17
It depends on your workflow. In many projects I am working, feature branches are rebased and altered. A forced push to a feature branch is considered okay.
This allows me to order commits in a way that related commits are clustered together. Sometimes I even squash multiple commits, as one commit fixes an issue or completes a prior commit.
If multiple people are working together on a feature branch, this is probably a bad idea. Never do this on main/master/trunk as forced pushes will disrupt the work of others.
For teams using trunk-based development reordering should be limited to the smaller, personal branches. Not on trunk.
I can’t think of any time when maintaining chronological authoring date was relevant.
Say you have five commits. You reorder them so that the two first ones come last. These commits might have been made two days before the other three commits. Not a problem. The author timestamps will tell as much.
The default of git-log(1) is to sort by committer date. This is a completely different field from the author date, what is being discussed here.
Reordering commits will (under normal operation, although I can’t say for sure about specific application) preserve the author date. But a history rewrite session like reordering commits will create new commits with new commit timestamps. If you reorder commits at 15:05 on a Sunday then the new commits will get the commit timestamp of 15:05 on Sunday.
Having commits in chronological order by commit timestamp is actually significant since git(1) relies on that property. But this is handled for you so don’t worry about it.
Be mindful that reordering commits is a history rewrite. The standard advice is to refer to git-rebase(1), section “Recovering from upstream rebase”.
History rewriting has consequences for whatever history (like other branches) which happened to be based on that rewritten history.
Rewriting history that you have published is either forbidden, perfectly okay, or something in the middle. This is determined by culture.
Some cite Linus Torvald’s thoughts thoughts on this as the final word, concluding that rewriting published history is wrong. This doesn’t take into account the cultural context:
In other words: they integrate changes from others. Which means part of their job consists of presenting contributors with some base that the contributors can make changes (patches) based on.
In the Git project, which also uses patches via email, the Git
maintainer maintains a master
branch which contributors can use to
base their work on. This branch is of course never rewritten.
So how do contributors work?
master
This workflow necessitates history rewriting of published history. Yes, even though the transportation format are patches and not branches or commits; the result is the same.
Another contributor could choose to base their work on version 3 of
some other patch series. But they will have to take responsibility for
that—the author of version 3 might release a version 4, then a version
5, and then the fifth version might get included into master
.
Of course the contributors could talk to each other and agree on how to rewrite or not rewrite their respective series. It comes down to culture and not hard and fast rules.
This can be related to a closed source project where everyone uses a shared remote to work on. Someone might publish a branch on that shared remote for review (like a pull request). Is there some universal rule that says how she can or cannot rewrite the branch history? In my opinion no, because in the world we live in (in my experience) there are plenty of people who think of such published feature branches as ephemereal and effectively private to the original author—not something to base your own work on.
You certainly can do that. You could base your work on their twenty commit rewrite of some component. Technically you can. Just like they technically could then rewrite it from out under you. There’s a lot that is technically possible but that culture dictates is or isn’t okay to do.
As the previous section has argued, although rewriting published history is problematic when you’re someone who people base their work on because of your role (e.g. you’re a Linux Kernel maintainer), the story is quite different if you’re an individual contributor who is working on their own thing; then other people should not (but again, depending on culture) just base their work on your work without telling anyone since you don’t integrate anything into the permanent history; that’s the job of the maintainers/integrators.
I wrote more about this perspctive in this answer.
git rebase
under the hood.