gitsvnbranchdevelopment-environmenttrunk

Where work with Git (Branches or Trunks)


I use SVN and have 3 environments

Actually, all the team work with the trunk.

We want to migrate to Git. Where we should work in the development environment?

We should work with branches and when we upload the code to production, reintegrate that branches to master (trunk)?


Solution

  • Git and SVN "think" very differently about commits and branches. Specifically, Git does not have a trunk.

    In other version control systems, branches and branch-names are very solid (and correspondingly quite heavy-weight) items and are in some cases totally inseparable. In Mercurial, for instance, once a commit is made on some particular branch, that commit is on that branch, and only that branch, forever. (In SVN, branches are extremely heavy-weight, but rather different and not as comparable as Mercurial is to Git. I like to use Mercurial as a comparator because both Git and Mercurial are distributed, while SVN is centralized. Distributed vs centralized also fundamentally changes how one uses version control. Git allows you to designate a central synchronization repository, but it's optional rather than mandatory.)

    By contrast, in Git, a branch name is a tiny, featherweight thing, of almost no importance at all. It's merely a human readable name for a commit, with one other special property. Git has two convenient, human-readable names for commits: branch names, and tag names. The biggest difference between them is that branch names are supposed to move—so much so that Git moves them automatically—and tag names are not, enough so that Git prevents you from accidentally moving them.

    Note this careful distinction, in Git, between a branch name, and a branch, er, something-or-other. Git does not quite make this distinction. There are at least two different things, both called "a branch". For more about this, see What exactly do we mean by "branch"? I will try to summarize that by saying that the actual history—the set of commits that make up a branch-as-a-thing-other-than-the-name—is formed by the commits themselves. Commits, once made, are permanent1 and unchanging. No one—not you, nor Git—can change a commit, because its hash ID—its "true name", which is entirely unreadable—is a cryptographic hash of its contents. Change even a single bit of those contents and the hash ID changes, so you have a new and different commit.

    Each commit contains, as part of its data, the ID of its parent (immediate ancestor) commit, or multiple IDs for multiple parent commits if the commit is a merge commit. It's these chains of IDs that form the branch-as-something-other-than-the-name—and in Git, this means that any given commit may be on many branches at the same time. In some cases, a commit may be on no branches at all!

    When you make a new commit, Git adds the new commit to the current branch, by writing the commit to the repository database with its parent set to the current commit. Thus, there is always2 a current commit, which is known as HEAD. Once the new commit is safely saved forever, Git then writes the new commit's ID into the branch name. HEAD is actually implemented as a file, but that file normally just contains the name of the current branch. It's the branch-to-commit-ID mapping that provides the hash ID of the current commit.

    Branch names can be added and removed at any time, though as noted in footnote 1, removing a branch name can—if those commits have no other name by which to find them—abandon commits to the Grim Reaper Collector.

    Because of all of this, Git should be used in a very different way. You can, and should, create new branches left and right, at the drop of a hat. When you have finished whatever it is that calls for the branch, merge it, rebase it, rebase-and-squash it, or throw it away, whatever you want: all of these are extremely fast. When you are done, whatever commits you have are the history, and you view that history by starting Git out with some branch name(s) or other starting-points (tags, or even raw commit hash IDs, for instance). It shows you those specific commits, and then their parents, and their parents' parents, and so on, all the way back to the first (parentless, or root) commit.


    1Permanent, that is, except for "abandoned" commits (and other Git objects). These are eventually garbage collected using git gc. Branch names serve an extra purpose: they protect commits from such collection. However, any name—branch name, tag name, stash from the git stash command, any of the notes names from git notes, and so on—will protect a commit. That commit then protects its parent commit(s), going back through all the commit history.

    2There is one exception to this rule. There has to be, to handle an empty repository, which has no commits at all. That exception is also used for so-called orphan or unborn branches (some of Git uses the first term, some uses the second), but most people only ever see the exception in a new, empty repository.