Do git tags apply to all branches?

2019-03-25 01:15发布

I'm getting my feet wet with git tagging, but my previous background is in Subversion, where "tags" were really just copies, not "real" tags...

If I add a tag to a git repo, is it applied to all branches or only the current one?

For example, if I currently have these branches (git branch -v):

* master deadbeef My master head comment
  dev    baddfeed My def head comment

And the currently checked out branch is master, as you can see. Now suppose I git tag -a TAGNAME, does TAGNAME apply only to deadbeef (master branch) or to baddfeed (dev branch) as well?

e.g., Say I subsequently switch to the dev branch (i.e., not the branch the tag was created on) before checking out the tag:

git checkout dev
git checkout TAGNAME

Do I then end up with a checkout of baddfeed or will the tag checkout (second line) switch me back to the master branch (where the tag was created) and give me a checkout of deadbeef? (Or third option, is my understanding of creating and restoring tags too flawed or too simplistic for the answer to be as simple as one of those two options?)

Also, does the answer to my question change any if I use a lightweight tag (git tag TAGNAME) instead of an annotated tag?

3条回答
疯言疯语
2楼-- · 2019-03-25 01:36

Having read the questions and your comments, I think the basic point that is confusing you here is what it means in git (vs other systems) to be "on a branch".

In git, a git checkout operation checks out some particular commit [but see footnote 1]. It also sets the special name HEAD so that it refers to that particular commit. But: there are two different ways that HEAD can specify a particular commit. It can be:

  • by ID (this is the not-usual case), or
  • by indirect reference to a branch name (this is the more-usual case).

If you do git checkout branchname, git sets up the second, usual case. If you then cat .git/refs/HEAD you will find it contains the literal string ref: followed by refs/heads/branchname [2]. That's what it means to be "on the branch": you have checked out the commit that the branch-name refers to, but also, HEAD is a "symbolic ref". If you make new changes and commit them, git will make the new commit, then "peel off" the branch label sticky-note and paste it onto the new commit you just added. That "moves the branch" to the newly added tip, and you're "still on the branch".

On the other hand, if you do git checkout tagname, git sets up the first, unusual case. It does the same if you check out by SHA-1 ID (those ea56709... style strings). In this case, the file for HEAD just has the literal SHA-1 ID in it. In this state, if you make a new commit, it gets added as usual, but there's no sticky-note-changing to move branch labels around. You're not "on a branch"; HEAD is not a "symbolic ref".

As far as the actual tags themselves go, they are merely names for commits. But wait, isn't that what a branch name is? Yes! The difference between a branch name and a tag name is that a branch name is expected to move, and git will move it automatically in that "on a branch" case. A tag name is not "expected to move" [3] and will never move automatically.


Footnotes:

[1] Just to confuse things (or because git's user interface is "evil", as some would put it :-) ) there are cases when git checkout does not alter HEAD, specifically when you ask it to check out particular pathnames.

[2] In very old versions of git, instead of ref: ... git used a symbolic link. The target of the link was the branch ID-file, e.g., refs/heads/master or whatever, so opening and reading the file got the commit ID, and you had to use lstat to detect "on a branch". This does not work on Windows and precluded "packing" refs (.git/packed-refs), hence the change to use ref: instead.

[3] The phrases "is expected" and "is not expected" should prompt you to ask: expected by whom? Git itself is OK with git users moving tags, it's the people using git (and often, scripts they write) that get confused by this. So don't move a tag unless you've first checked with other people sharing the repository.

查看更多
叛逆
3楼-- · 2019-03-25 01:53

Let's say you are in branch myBranch. And you create a tag called myTag

-----A-----B-----C-----D-----H-------I-----> master
                       \
                        \
                         \---E----F-----G-----> myBranch
                                        |
                                        myTag

The tag will be only on the commit object of the myBranch branch. I've worked with CVS too, and it tags revisions too, not all branches (but in CVS branches are special tags). I don't think.

If you checkout myTag, you will be in the commit G of the example. You can't create tags with the same name on differente branches. If you do it, you will move the tag.

There's no difference for an annotated tag related to this.

Note: when checking out tags, you end up with "detached HEAD" mode. Your changes will be lost unless you create another branch starting from the tag checked out.

查看更多
闹够了就滚
4楼-- · 2019-03-25 01:58

To make it simple:

If I add a tag to a git repo, is it applied to all branches or only the current one?

In Git, a tag is simply an alias to a commit id. When you add a tag, Git simply maps your tag name (the tag string) to a given commit id. Since the commit id is relevant to a specific branch (or branches when merging) the tag will be relevant only to that branch (and to any it was merged into).

More info can be found here:

http://git-scm.com/book/en/Git-Basics-Tagging#Creating-Tags

查看更多
登录 后发表回答