18

So, in many software you can do that easily using the mouse:

enter image description here

Although very useful, they aren't in chronological order anymore.

Is there anything wrong in doing that?

11
  • 19
    You can actually reorder commits with mouse move? Wow, that's new to me. Either way, this can only work if those commits are independent. And in such case I suppose the order doesn't matter.
    – freakish
    Commented Oct 25 at 9:24
  • 1
    Yes, for instance, in GitHub Desktop you can do it. docs.github.com/en/desktop/managing-commits/…
    – aybe
    Commented Oct 25 at 10:25
  • 18
    A note to readers: the answer to this question is yes, if you have already pushed your branch and someone else has pulled it down for the same reason you shouldn't rebase commits you have already pushed. So the big caveat to this question is that you are working on your own private tree -- local commit history. Commented Oct 25 at 12:49
  • 11
    And for those who are not familiar with git on the command line, the screenshot in this question is doing a git rebase under the hood. Commented Oct 25 at 13:43
  • 4
    Interestingly, the next generation of VCS tools, such as DARCS and Pijul, are built around the idea that reordering is such a fundamental operation that they build their entire system around being able to do so. It's tricky in Git, due to the reasons mentioned in the answer, but the fact that GitHub does this with a mouse click points strongly towards where the future is going!
    – Cort Ammon
    Commented Oct 26 at 15:52

7 Answers 7

39

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.

5
  • 11
    The chronological history is important as humans though in figuring out who did what when, and having out of order history, even if valid as code, can be confusing, more so than simply using a squash commit. So I wouldn’t totally discount the chronological history piece of this as it relates to using the commit history order to piece together the flow of development over time.
    – bob
    Commented Oct 25 at 18:08
  • 5
    (Looking over at my copy of Munkre's topology; "You hear that? You're fancy!")
    – Galen
    Commented Oct 25 at 20:17
  • 5
    +1. Sometimes I make a commit and only then discover that it depends on my pending changes - which I then commit and re-order so that a future bisect doesn't get broken. Commented Oct 26 at 9:14
  • 5
    @bob: I think there's a time-scale issue here. Reordering public history by moving independent commits several "months" ahead would be very confusing. Reordering commits on a private development branch, that nobody has seen, and which will hold only a few days worth of commits anyway, simply doesn't matter. Nobody cares about its history. If reordering it means smaller, more independent commits, then everybody wins. Commented Oct 26 at 11:43
  • 3
    @MatthieuM. I agree with you, particularly in the context of the distinction you made between public and private history. I think that’s the key. And you’re right that there’s an exception to every rule and sometimes you might need to reorder commits to make things less confusing. My point is only that for public history chronological order is generally important to maintain because it serves various important purposes to the dev team, and thus dependencies aren’t the only issue. And as you mentioned in your comment, the public/private distinction really matters.
    – bob
    Commented Oct 26 at 13:42
21

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.

Reordering Public History

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.

Reordering Private History

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.

Summary

  • 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.

6
  • "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"" yet the linux kernel still deals uses "patch series" as it's main way of accepting contributions. Producing and applying a patch series is effectively equivilent to a rebase. Commented Oct 25 at 19:58
  • 3
    @PeterGreen: the difference is a patch series creates new history that was never shared amongst other developers. Read Linus' full reply in the mailing list message I linked to. It is quite lengthy, but has a lot of really good information in it. Commented Oct 25 at 20:00
  • +1 for inspiring me to edit my answer. The number one rule of rebasing is to do it in private. Commented Oct 25 at 22:11
  • 5
    In my team, our Working Agreement says that "feature" branches are considered fair game for rebasing unless any dependent work is agreed with the branch owner. If there's dependent work, then rebasing is allowed only with approval of the affected developers (and, implicitly, the owner's help if it causes problems). Commented Oct 26 at 9:20
  • 2
    I would add that as long as your code is pushed only to a branch clearly marked as yours, it's usually fine to still treat it as private. E.g. I rewrite history of my own MR/PR branches all the time, and I expect that if someone branches their work from there, they will either let me know, or deal with it on their own.
    – Frax
    Commented Oct 28 at 13:03
7

I think there are different ideas of what a project's history should mean.

  1. The history of the development process. Who did what, based on what, in what order.
  2. The history of the "main repository", what did this project look like a week ago? a year ago? last night?
  3. A logical set of steps that takes us from A to B.

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.

3

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.

2

1) Commit reordering is history rewriting

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.

2) History rewriting can always be harmful

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!

3) Every history rewrite must be followed by a full test suite run on every rewritten commit.

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.

6
  • #3 is a moot point if you're running CI on every pull request, which you definitely should be.
    – Alexander
    Commented Oct 26 at 18:00
  • 1
    @Alexander That's a good implementation of #3, definitely. At least as long as it's configured to actually test every submitted commit, not just the endpoint of a committed branch. I don't know whether every CI solution does this, though. It seems like a corner that's likely to be cut. Commented Oct 26 at 20:11
  • 2
    Well in GitHub's approach to git, commits shouldn't necessarily be green. The atomic unit of green changes is a pull request (and the corresponding merge commit it creates), not the individual commits themselves. It's perfectly reasonable to have an intermediate commit that isn't green, and even necessary in some cases. E.g. for big file moves+changes, the only way to preserve file history is to do the moves in a separate commit from the changes. They can't be squashed, otherwise the "file moved" heuristic fails, and they're seen as file deletions + new file creations
    – Alexander
    Commented Oct 26 at 20:46
  • @Alexander Yeah, such commits can be a valid reason for a non-building commit. However, the result of every commit with actual changes should build with the test suite completing as expected. That's the only way that people can actually put 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
  • It's lesser known, but that's where 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
    – Alexander
    Commented Oct 26 at 21:17
0

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.

0

Chronological order

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.

It doesn’t even matter for git-log(1)

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.

Reordering commits: a history rewrite

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 published 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:

  1. This is in the context of “pulling changes” in the Kernel, i.e. using git-pull(1), which is something that Kernel (subsystem) maintainers do
  2. These people maintain Linux trees which other people base their work on

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?

  1. They publish a single patch or a patch series (multiple)
  2. They get feedback
  3. They publish new versions of those patches based on the feedback
  4. Eventually a version of those might get accepted for inclusion into 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.

Rewriting as an individual contributor is often okay

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.

Not the answer you're looking for? Browse other questions tagged or ask your own question.