71

I've heard in several places "Don't make large commits" but I've never actually understood whats a "large" commit. Is it large if you work on a bunch of files even if there related? How many parts of a project should you be working on at once?

To me, I have trouble trying to make "small commits" since I forget or create something that creates something else that creates something else. You then end up with stuff like this:

Made custom outgoing queue

Bot
-New field msgQueue which is nothing more than a SingleThreadExecutor
-sendMsg blocks until message is sent, and adds wait between when messages get
sent
-adminExist calls updated (see controller)
-Removed calles to sendMessage

Controller
-New field msgWait denotes time to wait between messages
-Starting of service plugins moved to reloadPlugins
-adminExists moved from Server because of Global admins. Checks at the channel,
server, and global level

Admin
-New methods getServer and getChannel that get the appropiate object Admin
belongs to

BotEvent
-toString() also show's extra and extra1

Channel
-channel field renamed to name
-Fixed typo in channel(int)

Server
-Moved adminExists to Controller

PluginExecutor
-Minor testing added, will be removed later

JS Plugins
-Updated to framework changes
-Replaced InstanceTracker.getController() with Controller.instance
-VLC talk now in own file

Various NB project updates and changes

---

Affected files
Modify  /trunk/Quackbot-Core/dist/Quackbot-Core.jar
Modify  /trunk/Quackbot-Core/dist/README.TXT
Modify  /trunk/Quackbot-Core/nbproject/private/private.properties
Modify  /trunk/Quackbot-Core/nbproject/private/private.xml
Modify  /trunk/Quackbot-Core/src/Quackbot/Bot.java
Modify  /trunk/Quackbot-Core/src/Quackbot/Controller.java
Modify  /trunk/Quackbot-Core/src/Quackbot/PluginExecutor.java
Modify  /trunk/Quackbot-Core/src/Quackbot/info/Admin.java
Modify  /trunk/Quackbot-Core/src/Quackbot/info/BotEvent.java
Modify  /trunk/Quackbot-Core/src/Quackbot/info/Channel.java
Modify  /trunk/Quackbot-Core/src/Quackbot/info/Server.java
Modify  /trunk/Quackbot-GUI/dist/Quackbot-GUI.jar
Modify  /trunk/Quackbot-GUI/dist/README.TXT
Modify  /trunk/Quackbot-GUI/dist/lib/Quackbot-Core.jar
Modify  /trunk/Quackbot-GUI/nbproject/private/private.properties
Modify  /trunk/Quackbot-GUI/nbproject/private/private.xml
Modify  /trunk/Quackbot-GUI/src/Quackbot/GUI.java
Modify  /trunk/Quackbot-GUI/src/Quackbot/log/ControlAppender.java
Delete  /trunk/Quackbot-GUI/src/Quackbot/log/WriteOutput.java
Modify  /trunk/Quackbot-Impl/dist/Quackbot-Impl.jar
Modify  /trunk/Quackbot-Impl/dist/README.TXT
Modify  /trunk/Quackbot-Impl/dist/lib/Quackbot-Core.jar
Modify  /trunk/Quackbot-Impl/dist/lib/Quackbot-GUI.jar
Modify  /trunk/Quackbot-Impl/dist/lib/Quackbot-Plugins.jar
Modify  /trunk/Quackbot-Impl/lib/javarebel.stats
Add /trunk/Quackbot-Impl/lib/jrebel.info
Modify  /trunk/Quackbot-Impl/nbproject/private/private.properties
Modify  /trunk/Quackbot-Impl/nbproject/private/private.xml
Modify  /trunk/Quackbot-Impl/nbproject/project.properties
Modify  /trunk/Quackbot-Impl/plugins/CMDs/Admin/reload.js
Add /trunk/Quackbot-Impl/plugins/CMDs/Operator/hostBan
Modify  /trunk/Quackbot-Impl/plugins/CMDs/Operator/mute.js
Modify  /trunk/Quackbot-Impl/plugins/CMDs/lyokofreak/curPlaying.js
Modify  /trunk/Quackbot-Impl/plugins/CMDs/lyokofreak/lfautomode.js
Modify  /trunk/Quackbot-Impl/plugins/listeners/onJoin.js
Modify  /trunk/Quackbot-Impl/plugins/listeners/onQuit.js
Modify  /trunk/Quackbot-Impl/plugins/testCase.js
Add /trunk/Quackbot-Impl/plugins/utils/whatsPlaying.js
Modify  /trunk/Quackbot-Impl/src/Quackbot/impl/SandBox.java
Add /trunk/Quackbot-Impl/vlc_http
Add /trunk/Quackbot-Impl/vlc_http/current.html
Modify  /trunk/Quackbot-Plugins/dist/Quackbot-Plugins.jar
Modify  /trunk/Quackbot-Plugins/dist/README.TXT
Modify  /trunk/Quackbot-Plugins/dist/lib/Quackbot-Core.jar
Modify  /trunk/Quackbot-Plugins/nbproject/private/private.properties
Modify  /trunk/Quackbot-Plugins/nbproject/private/private.xml
Modify  /trunk/Quackbot-Plugins/src/Quackbot/plugins/JSPlugin.java
Add /trunk/Quackbot-Plugins/vlc_http
Add /trunk/global-lib/jrebel.jar

Yea....

So for questions:

  • What are some factors for when a commit becomes too large (non-obvious stuff)?
  • How can you prevent such commits? Please give specifics
  • What about when your in semi-early stages of development when things are moving quickly? Are huge commits still okay?
4
  • lists.madduck.net/pipermail/vcs-home/2010-September/000276.html describes a case where the data files themselves are just too big to be effectively stored in the repository. (But I'm sure that's not what you're talking about here.)
    – Ken Bloom
    Oct 11, 2010 at 4:00
  • Not constructive????? I just learned a ton here! Mods, please stop your draconian question closing!
    – richard
    Mar 29, 2015 at 7:08
  • What you never seen a single commit with hundreds of files, that any cut up thereof would not compile?
    – Joshua
    Mar 31, 2015 at 23:33
  • This post could be opened again if it was edited to ask for objective metrics. It can certainly be answered in an objective way, albeit (also) opinionated. Apr 28, 2023 at 14:15

19 Answers 19

70

To me, I have trouble trying to make "small commits" since I forget or create something that creates something else that creates something else.

That is a problem. It sounds like you need to learn to break down your work into smaller, more manageable chunks.

The problem with large commits are:

  • In a multi-person project, a greater chance that your commits will cause conflicts for other developers to resolve.
  • It is harder to accurately describe what has been done in log messages.
  • It is harder to track the order that changes were made, and hence to understand the cause of problems.
  • It increases the probability of losing a lot of uncommitted work.

Sometimes large commits are unavoidable; e.g. if you have to change a major API. But that's not normally the case. And if you do find yourself in this situation, it is probably a good idea to create a branch and do your work in there ... with lots of small commits ... and reintegrate when you are finished.

(Another case is when you do an initial import, but that's NOT problematical from the perspective of the issues listed above.)

3
  • 7
    +1, definitely learn how to break it down into smaller chunks. When looking for a bug, smaller commits are easier to manage. After the first few times of staring at a large commit, you'll get the habit :P Oct 10, 2010 at 11:15
  • 2
    If needed at the end of the series of small commits, you can add a label / tag which includes a long summary description. This effectively applies a baseline at the point where your large block of work is done, before you re-integrate or start on some form of more formal testing (should that be part of how you/your business works). I WOULD ADD: Making large scale changes (as you seem to be suggesting) down a development branch is a hugely good idea. It prevents pollution of your main stream with great piles of crap and makes quick-fix service packs,etc easier to create if they are needed. Jan 20, 2011 at 0:15
  • 1
    Also, smaller commits = smaller diffs for people who review PRs commit-by-commit Sep 20, 2018 at 15:07
43

The Single Responsibility Principle.

Every source control commit should serve only one purpose. If you have to put the word "and" or "also" in your summary, you need to split it up.

It's very common to end up with lots of separate unrelated or semi-related changes in your working copy. This is called the "tangled working copy problem," and it's actually very hard to avoid even for disciplined developers. However, Git and Mercurial both give you tools to resolve it -- git add -p or chunk selection and Mercurial Queues in TortoiseHg respectively.

1
  • 2
    this is a good principle; however in practice, I would still accept commits that do multiple things (especially if they are related) and if the commit size is small enough; To compensate I'd recommend being a master of interactive rebase to rewrite non-pushed history until it's good & clear.
    – Rivenfall
    Feb 2, 2016 at 10:21
26

Imagine that the client asked to have a particular change made - for example to add a rule that something or another can't be done within two days of the "whatever" date. Then, after you've made the change, they change their minds. You will want to roll back your commit. If it's all mushed in with some things about changing the sort order of unrelated reports, your life is a misery.

One work item, one changeset. One request from the client, one changeset. One thing you might change your mind about, one changeset. Sometimes that means it is one single line of code. Sometimes it is ten different files including the database schema. That's fine.

2
  • Agree completely with the "1 line/10 files" . There too many variables to answer this question by a standard set of laws Jul 30, 2012 at 8:00
  • 7
    The only thing I'd add is that sometimes it makes sense to go even smaller than "one request, one changeset". Larger requests should be broken down into smaller, incremental changesets. (As mentioned in another answer, development on a branch might facilitate this) Ultimately, I might adapt the aforementioned mantra as such: "One request, one (or more!) changesets".
    – rinogo
    Feb 25, 2013 at 18:13
10

Large commits are when you have tons of changes that don't really all go in the same bucket. If I change the controller logic, then the database connection model, then some misc. scripts, I shouldn't be bundling it all under one commit.

Prevention is making commits according to what you're completing. In the above example I would commit after the controller logic, after the database work, and after the scripts. Don't put off committing simply because you know what changed. Other people will look back at your "Changed stuff" commit log message and wonder what you were smoking.

Initial imports are probably the biggest commits you should ever have. Setting up a system from scratch? Sure have a few big commits. After you've leveled it out though it's time to keep things organized.

7

If you know you are going to be working on a large chunk of code beforehand, I would suggest creating a branch for your specific feature while periodically pulling code down from the mainline to make sure your code stays in-sync. When you're done working on the branch merge all your changes back into the mainline. This way other team members will not be surprised and/or annoyed when they see a huge commit. Also, there's a much less chance of breaking things. Keep practicing to break things down into smaller commits. With time it will become second nature.

7

This example shows a commit that is too large.

As a rule of thumb, describe the change in one sentence or one line of text. (Based on this rule, the commit should be broken into 10-15 smaller ones.) If you can't adequately comment a commit in one line, then it's already too large.

To practice smaller commits, take notes in your notepad (or in Notepad) of what you've already changed or added. Commit before it becomes a long list or before you make a code change unrelated to what you already have in the notepad.

2
  • The Linux kernel repository has lots of good examples of violating this rule -- they often have lots of explanation about the cause of the bug or the rationale for the fix in the body of the commit message. A reasonable version of your rule would be "you should always be able to explain the main point of a commit in 1 sentence."
    – Ken Bloom
    Oct 11, 2010 at 3:33
  • @Ken: my goal here is to help the person asking the question, not to come up with a rule covering all pre-existing source code repositories in the world.
    – azheglov
    Oct 11, 2010 at 12:55
6

In my field (physics modeling), I discover a bug in the output today that wasn't in the repository as of 6 months ago. When this happens, I will do a binary search on revisions:

  1. Run model from 3 months ago
  2. If bug is still in output, run the model from 4.5 months ago
  3. ... repeat until I find the commit that yields in bad output

When the bug was introduced in a monstrous commit, I have to sit with a fine-toothed comb to find the source of the problem. If the commit touched a small number of files, it's less painful to track down the line(s) of code that introduced the problem.

I would recommend breaking down your problem into a series of smaller tasks (ideally put each task in a bug tracker). Make a commit as you complete each task (and close that bug/feature in your bug tracker).

1
  • Months between commits sound exactly like massive commits in most modern methodologies...
    – Rig
    Mar 24, 2013 at 15:31
5

It isn't the size of the commit that really matters, it is the scope of the change that should determine how your commitments are organized.

You might, for instance change every instance of __macro1 to __macro2 in a large code base, which changes 200 files. 200 commitments would not be sane in that case.

What you want to end up with is being able to pull the repository at any single revision and having the build work. Did you change from libfoo to libbar? I hope that change includes updating your build scripts and Makefiles as well (or whatever is applicable).

Sometimes, you might need to make a series of experimental changes that accomplish one thing, in which case, you have to determine what scope is more important to you if you need to revert later. Does one depend on the other? Commit them all at once in one single revision. Otherwise, in that case, I'd suggest one commit per change. You should be doing something like that in another branch, or in another repo anyway.

While yes, you do have the power to revert a single file to a previous revision (thus backing one file out of a larger commitment), doing so really screws up tools like bisection later on down the road, and pollutes the history.

If you stop and think "Ok, tests pass, I think this works .. but if it goes bad, can I easily back it out?" .. you'll end up making sensible commitments.

4

The thing to grasp here is that "Large" in this context is about the number distinct changes not the physical size of the commit (although generally the two will go hand in hand).

Its not so a question of "don't make large commits" as do make small commits - the ideal being to commit small self contained changes.

Its clear from the changelog that you've got a series of things that could have been committed separately (and safely) and therefore its fairly self evident that its too large.

The reason that this can be a problem is that your last commit is your reference point for the changes you're currently making and if, for example, you get the first bit right and then get the next bit wrong you've no easy way to roll your work back to the point where you started making mistakes (BTDTGTTS).

Of course sometimes changes just are big - large scale refactoring - and as is suggested by others this is where you need to branch, that way although your individual commits might notionally break things they are separated from the main development trunk so that isn't a problem and you get to continue to commit early and often.

One more thing - if something comes along in the midst of your work that requires more immediate attention you need to change it separately (ideally in a completely distinct set of folders) and commit it separately.

The real challenge in all of this is not the mechanics its the mindset - that a commit is not just a backup copy that you make every now and then but that each commit is an inch-pebble along the way and that there's nothing wrong with lots of small commits and that munging different things together in a mob commit is as bad a munging vaguely related bits of functionality together in a lump of code.

4

At the very least, train yourself to commit whenever you think to yourself "I like my progress so far, and I don't want to lose it if the changes I'm about to make are a disaster." Then you have the option to take advantage of the VCS to blow away any dead ends that you tried or special debugging code you added to track down a problem. (e.g. with git reset --hard or rm -rf *; svn update)

2

There's no hard and fast rule, no dividing line past which your commit is too big.

There is however a guideline that smaller commits are better, within reason (ie committing every line is probaby excessive).

I keep these kinds of guidelines in mind:

  • A single commit should include changes for just one bug fix
  • A single commit shouldn't include more than half a days work
  • A single commit shouldn't break the build

Of course - these are what I keep in mind - YMMV. Different developers favour different levels of granularity.

1

The smaller the commit is, the easier it will to find exactly where a potential regression comes from.

Ideally a commit should be atomic, in the sense of the smallest coherent change to the code base (related to a bug, feature, etc).

As for specific tips to keep the size of commit small, it depends greatly of your VCS and how it is setup : you must be able to commit locally or work in your own branch on the server.

The key is to commit to your "private" branch every time you do an atomic change, and then you can merge regularly your branch, for example every week.

Using a dvcs, your workflow could look like that :

code code code
git commit       // create commit locally with meaningful message
code code code
git commit       // create commit locally with meaningful message
code code code
git commit       // create commit locally with meaningful message
...
git push         // push your previous commits to the team server

Using a centralized vcs :

svn copy trunk my_feature_branch  // create your private branch
svn co my_private_branch          //
code code code
svn commit                        // commit on your private branch with meaningful comment
code code code
svn commit                        // commit on your private branch with meaningful comment
code code code
svn commit                        // commit on your private branch with meaningful comment
...
svn merge my_feature_branch trunk  // all your previous commit are merged to main/master branch
0

You have probably heard the saying that perfection is when you cannot take anything more away. That should also describe your standard for commit size.

It depends on your project where that "perfect" size is. If you are shipping to external customers, a good size might be the smallest increment that you would be comfortable shipping if you didn't finish the next one on time. If you are building internal, frequently deployed applications, the best size might be the smallest increment that doesn't break anything (and gets you closer to where you want to be).

Modern version control systems help you create good commits with easy branching, interactive rebasing, staging area, etc.

0

Commit messages should be one line only (and for git max 60 chars). The amount of code being committed must be small enough to keep the descriptive message within that limit.

I tend to commit everytime (even more so now we've switched to git) I have a chunk done, as it allows to capture the "why" things were done in this way.

3
  • That seems a little, extreme. A commit should say what you fixed and what changed. This way you can find a misbehaving commit if something broke, and prove that you fixed something.
    – TheLQ
    Oct 10, 2010 at 19:12
  • @TheLQ: Again, I bring as an example many of the commits on the Linux kernel, where a long commit message serves to communicate the rationale for a particular change to other developers.
    – Ken Bloom
    Oct 12, 2010 at 0:06
  • @TheLQ, that's how things work well for us. Remember, you need to have the "why"s somewhere...
    – user1249
    Oct 12, 2010 at 0:26
0

Sometimes you've been working all day on several different logically distinct chagnes, and you forgot to commit your code in between. Using git citool can be very helpful for breaking up your work into nice bite-sized chunks at the end of the day, even if you weren't so careful during the day while you were working.

git citool can let you select which specific hunks of a file (or which specific lines) to commit in a particular commit, so you can break up (non-overlapping) changes made to the same file into several commits.

(It seems that you use Subversion. I don't know of a tool that does this for Subversion, but you could look into using git-svn, the Subversion adapter for git, which will change your life.)

2
  • Yea that was one of things I miss about git: The ability to commit only part of a file. I think though in the end it would be a mess since I would commit 1 method but not the new one that it depends on, breaking something.
    – TheLQ
    Oct 11, 2010 at 21:49
  • @TheLQ: well, that's what you have to do if you want to organize your commits into logical chunks: either get very disciplined about committing early and committing often (and don't be afraid to git rebase to join commits that are really part of the same revision) OR learn to accurately go through git citool with a fine-toothed comb to split things into logical parts when you're ready to commit at the end of the day.
    – Ken Bloom
    Oct 12, 2010 at 0:04
0

The large the commit, the more likely you'll break the build and get paid out by the rest of your team. I try commit changes twice a day. Just before lunch and before I go home. So @ 12pm and 4:30pm I try get everything working and ready to commit. I find this practice works for me.

0

To answer your questions:

1) For me the standard commit is considered big if it's doing more than one thing. By thing I mean fixing a bug or adding a feature.

2) Prevent such commits by making it a habit and a rule to commit whenever you finish something.

3) In the semi-early stages of development, I allow the commits to include the first creation of the files which will be used later.

I would like to note that by finished I mean that all the bugs you can identify have been fixed and you won't break the build by committing.

Yes, this generates a large number of commits, but it lets you rollback exactly what broke things instead of having to roll back a large series of change which were committed at the same time where only one of the changes is causing an issue.

I would also like to point out that the rules change a bit for distributed version control systems (DVCS) like Mercurial and Git. In the case you're using one of those, you commit whenever you've made a change, but haven't tested it yet and then you push to the central repository when it's working. This is preferable as it lets you revision more changes to your code without worrying about breaking the build.

0

In my case I am trying to commit files of a server to the repository system (SVN). This is the initial commit and don't want to download it as it is a really large project (a few GB) and I want to do the initial commit off the clients server.

The issue is that the client is on a shared server, the svn client is killed (or any other software) if it runs more then a minute.

One alternative would be to download the project on my computer and do the initial commit from there but I am interested to know if there is a option in SVN to break the large commit into more, something similar to transactions methods.

The developer before me never used a version control system.

-1

The company I work for forces a peer code review for every commit. Therefore, any commit that makes it difficult for a peer to figure out what is going on and review in a reasonable amount of time, is too large.

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