3

I was tasked to organize the repository in the way that aside other branches there would be a dedicated branch, where we would only store the commits of released version. Below is the simplified scheme of what I would like to achieve:

| trunk    |           | releases |
|----------+-----------+----------|
| commit 1 |           |          |
| commit 2 | v0.1 ---> | tag 1    |
| commit 3 |           |          |
| commit 4 |           |          |
| commit 5 |           |          |
| commit 6 | v0.2 ---> | tag 2    |
| commit 7 |           |          |
| commit 8 |           |          |
| commit 9 |           |          |

This is a little bit too advanced for me now, so I'd appreciate some guidance on how to do that! I'm not really sure on how would I be able to have the second tag in the "releases" branch w/o having to import all the intermediate commits. Is that possible at all?

Also, if you have a better scheme for achieving the same goal (the goal being to have a dedicated branch for releases only), please do not hesitate to advise!

  • 1
    Why do you need a separate branch? Why not just have release tags on master? – beatgammit Jul 11 '13 at 06:47
  • @tjameson 1. I'm not sure. 2. That's the task I've been given :) 3. The argument was made that it must be easier for others to only know the one "address" to which to turn to in case they need a released version... well, let's call it a corporate logic concern, of which I'm not the master yet. –  Jul 11 '13 at 06:50
  • 1
    @tjameson: this looks like a part of git-flow (http://nvie.com/posts/a-successful-git-branching-model/). I don't know any way to enforce only tagged commits in a branch via git, but as a convention it works quite well. – fjarri Jul 11 '13 at 07:09

4 Answers4

6

That wouldn't make sense, since the tagged commits represents the state of a branch, composed of the commits and its predecessors.

Isolate just the tagged commits would write a very different history, since those commits would miss their ancestors: it is the sequence of said ancestors plus the tagged commits which leads the code base to a particular state.

The simpler approach is to make sure you tag your release commits in one dedicated branch (master for instance).

Then, a simple git show-ref --tags can list the comimts referenced by those tags.

Or you can create a branch from any of those tags (for bug fixing a release)

git checkout -b newbranch v1.0

And you can deduce the last release tag from any commit (git describe --long).


The OP wvxvw adds in the comments:

but the branch with releases only to store the history of special commits (the tagged ones) made into it, and point to other branches as being sources from where the commits came from

That would be possible by merging the tag commit in a "release branch", with a merge which keeps "theirs" (ie the source of the merge, meaning the source of the tagged commit)

--x--x--x--x--x--x--x
 (v1)     (v2)
   \        \ 
----y--------y--
Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • +1 Hacks like `git commit --allow-empty` on the release branch would kind of solve the problem, but the tags would still show up in the output of `git tag`. The benefit of the --allow-empty option is for `git describe` to work differently on different branches. – beatgammit Jul 11 '13 at 06:58
  • I think there is some confusion about "not storing commits". What I mean is that I want the history of commits to be stored at where it actually happened - in the relevant branches, but the branch with releases only to store the history of special commits (the tagged ones) made into it, and point to other branches as being sources from where the commits came from. I wouldn't imagine it to be possible to somehow have a totally independent commit incorporating changes from several other commits all at once... not that it would hurt, but I don't think it is possible :) –  Jul 11 '13 at 08:27
  • Thanks! I was trying to read the other question you linked to, but in the meantime the requirements have changed... Also, I don't really think I understood the other answer, but I'm rather happy I don't have to do this any more :) Thanks for your time though! –  Jul 11 '13 at 10:57
3

This doesn't really make sense. In git, a branch name is simply a label pointing to a specific commit, in which the name "moves forward" automatically when adding a new commit:

C1 -- C2 -- C3         <-- label X
              \
                D1     <-- label Y

If you add a commit "on branch X" (i.e., where label X points), you get this:

C1 -- C2 -- C3 -- C4   <-- label X
              \
                D1     <-- label Y

A git tag is either "lightweight" or "annotated". The difference is that the first one is just a name—just a label, like X and Y here—pointing to a specific commit, while the second is a label pointing to a special kind of object stored in the repo, with the object pointing to the commit (this allows the annotated tag to carry more data than the lightweight tag: that extra data is stored along with the commit-ID to which the tag points). For our purposes here, though, the distinction is not really relevant.

Suppose I tag commits C4 and D1 with tags TC and TD. I get:

C1 -- C2 -- C3 -- C4   <-- branch X, tag TC
              \
                D1     <-- branch Y, TD

If I add more commits to "branch X" or "branch Y", what happens is that the branch label moves, while the tag label stays fixed. For instance, if I add commit C5 (on "branch X"), X will point to C5, but TC will still point to C4. However, if I do this by merging "branch Y" into "branch X"—this will be the new commit C5—I'll get:

                   .------------- tag TC
                   v
C1 -- C2 -- C3 -- C4 -- C5   <-- branch X
               \      /
                  D1         <-- branch Y, tag TD

I can now (without any complaint from git) delete "branch Y" entirely as its commits are "contained in" (which really means, reachable from) branch X and tag TD. If I do that, "tag TD" is not "on branch Y" at all, as branch Y does not exist. Even if I leave branch Y in place, tag TD is still contained in "branch X"! Starting at C5 and going to the second parent gets me D1, which is where TD points.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Similar to my answer, but with more ascii-art, so +1 ;) – VonC Jul 11 '13 at 07:24
  • I think that what I really need is the opposite of your example... I was thinking more in terms of `--no-ff` commit on the master branch, where it would then not store the history from the branch, from which the original commit came from. Your post is interesting, but unfortunately doesn't advance me to achieving the goal :( But thank you nevertheless! –  Jul 11 '13 at 08:23
  • @wvxvw: You probably *do* want `--no-ff` merges into whichever branch(es) is/are the ones that you're using for releases. But the tags still aren't "on the branch" in any useful sense. *Commits* are the things "on"—more properly, "reachable from"—a referencing label. In effect, tags live at the same level as branch names, outside that space. (That's why they are found in files in `.git/refs/tags/` in the same way that branch names are in `.git/refs/branches/`. In fact, a tag may sometimes be the *only* external name for a commit, keeping the commit from being garbage collected.) – torek Jul 11 '13 at 08:29
1

You need to understand some things about git first:

  • A git repo can be seen as a bunch of commits. Each having one or two (merge) parent commits.
  • A tag is just a pointer to one of those commits. It's fixed.
  • A branch is also a pointer to one of those commits. But it will always point to the last commit that you did while you where on that branch. So it's a "dynamic" pointer.

Maybe this explains, why what you want doesn't really make sense: Tags are independent of branches.

Michael Härtl
  • 8,428
  • 5
  • 35
  • 62
  • Minor point: "octopus merges" have more than two parents. (Mercurial restricts merges to two parents at a time; to get the effect of an octopus merge you do a sequence of pair-wise merges instead. It works out the same in the end, of course.) – torek Jul 11 '13 at 07:07
  • and root commits have zero parents so commits in git have zero or more parents. – CB Bailey Jul 11 '13 at 07:18
0

This is a branching model that I've used before to keep dev, bugfixes, features, and releases all separate, but coordinated. It may help you.

http://nvie.com/posts/a-successful-git-branching-model/

kmatheny
  • 4,042
  • 1
  • 19
  • 12