问题
I understand one always needs a message for committing the changes but when and why do I need to also tag a commit? Let's say I made some changes and I commit using
git add -A
git commit -m "add feature 1"
and now
git tag -a -m "includes feature 1" v0.1
The question is when does this make sense.
回答1:
It would make sense to specify a tag when you release a version of the software you're producing.
You could then do:
git tag -a v1.0 -m "Release Version 1.0"
Like my comment mentioned, you do not have to tag after every commit, like you mention in your post, you can also create lightweight tags, if you don't want to include a message. This would look like:
git tag v1.0
Hope this helps.
回答2:
First, let's define the difference between a commit, a branch name and a tag name. Then I'll outsource the rest of this to the existing question and excellent answer at What is the difference between an annotated and unannotated tag? Note that a lightweight tag (un-annotated) does not take a message; only an annotated tag takes a message.
Defining a commit
A commit is an object in a Git repository. Git repository objects are mostly-permanent and completely read-only, and are consistency-checked so that they are incorruptible. There are four kinds of objects: commits (which we'll describe in detail in a moment), trees, blobs, and annotated tag objects. All four kinds are identified by hash IDs.
A blob is, very roughly, where Git stores your file's contents: if your README
file says I am a readme file
, the string I am a readme file
is stored as a blob. A tree object is, slightly oversimplified, the thing that stores the file's name along with the blob hash ID of the file's contents. Hence, in order for commit 1234567...
to store your README
file, Git has a tree that says "file README
has contents from blob c3c2a8983de728ffcf8f0ccaad014349925f23e6
".1
In any case, each commit stores one tree hash ID, which is the saved snapshot of your source files2 at the time you made the commit. It also stores the hash ID of the parent commit(s) of this commit, so that Git can work backwards through the history to find all commits. It stores your name and email address and a timestamp, as the person who made the commit and when.3
After these required items—tree, parent(s), author, and committer—Git adds your log message, which is entirely arbitrary as far as Git itself is concerned. You don't have to supply any log message at all, and if you do supply one, it does not have to make sense. It will, however, be shown to you, and to other people, when they explore the history—the commits—stored in the repository.
Commits form chains, which is history
Because each commit records its earlier parent commit, the set of commits forms a chain that can be read backwards:
A <-B <-C <--master
Git can start with a name, like the branch name master
, to get the hash ID of commit C
(whatever its real hash ID may be: I use C
here as shorthand instead of the actual hash). This hash ID is unique to commit C
: no other commit anywhere has that same hash ID.4 Commit C
itself stores the hash ID of commit B
, so from C
we can find B
. B
stores the hash ID of commit A
, so from B
we can find A
.
This particular repository has only three commits: A
is the root commit, the first one we ever made, so it has no parent, and that lets us stop following history.
Branch and tag names find commits
But this also tells us what a branch name is and does: A branch name is a name that identifies a commit, by the commit's hash ID. A tag name does the same thing. So now we might wonder: what's the difference? When should we use a branch name, and when should we use a tag name?
Aside from the issue of name spaces,6 the main difference is that Git will let us get "on" a branch name, using git checkout
. Once we are on such a branch, creating a new commit has a side effect: it changes the hash ID associated with the branch name. If we check out master
and make a new commit, the parent ID stored in the new commit is the ID of C
. Whatever the new commit's hash ID is, as computed by Git, that new hash ID goes into the name master
, so that we now have:
A--B--C--D <-- master
The name master
now points to new commit D
, which points back to C
, which points back to B
, and so on. The branch has grown, and the branch name points to the latest commit. Each commit points back to a previous one.
You cannot get "on" a tag like this. The tag name v1.0
, once it's set to point to commit C
, continues to point to commit C
, forever. So this is the main difference between a branch name and a tag name in Git: A branch name changes over time, and even does this automatically when you make commits. A tag name should never7 change.
1c3c2a8983de728ffcf8f0ccaad014349925f23e6
is the hash ID of the content that reads I am a readme file
. You can find this by running the following shell command:
$ echo 'I am a readme file' | git hash-object -t blob --stdin
c3c2a8983de728ffcf8f0ccaad014349925f23e6
Note that any blob object anywhere in any Git repository in the universe has this hash ID if it has this content! The content must consist of just those 19 bytes, including the terminating newline (with no carriage-return).
2More precisely, it's the saved index, aka staging area aka cache, as written out by git write-tree
.
3Git stores these twice, once as the author of the commit and again as the committer. The two become different if you are, e.g., taking someone else's commit that they emailed you, and inserting it into a repository: then the author is the one who sent you the commit, but you are the committer. There are other situations in which the two become different. For the most part, nobody cares much, but run git log --pretty=fuller
to see both.
4Hash IDs can repeat in different repositories, but only if the two repositories are never brought together. To make sure that commit hash IDs are unique, Git includes those time-stamps. This ensures even if you make the same commit you made before, with the same tree and same parent and same log message, if you make it at a different time, it's a different commit. (If you make it at the same time, then it's really exactly the same, and why do you care if you make it once or make it twice?5)
5There is a potential reason to care, but it's quite obscure and mostly irrelevant.
6Technically, a branch name is a reference that starts with refs/heads/
, e.g., refs/heads/master
is full spelling of the master
branch. A tag name is a reference that starts with refs/tags/
, such as refs/tags/v1.0
. These are the reference name spaces, which include refs/remotes/
, refs/notes/
, refs/replace/
, and refs/stash
as well.
7Well, hardly ever. Some people really want a "floating tag". You can do this in Git, if you are careful and know what you are doing. There's no real point, though: if you want a name that moves, use a branch name. That's what they do. If you want a name that doesn't move, use a tag name. That's what they do.
来源:https://stackoverflow.com/questions/49414218/difference-between-tag-and-commit-message