I am trying to understand tags.
If a tag is a pointer to a commit. And a commit is composed of staged changes.
Does a tag contains previous commits ? (I know it does because I experimented it). But how come ?
I am trying to understand tags.
If a tag is a pointer to a commit. And a commit is composed of staged changes.
Does a tag contains previous commits ? (I know it does because I experimented it). But how come ?
if i create a tag using a specific commit. will this tag contains all the commits (changes) before the specific commit ?
A tag is a label referencing a commit.
See "Why should I care about lightweight vs. annotated tags?"
I mentioned in 2011 in "How does git store files?" that commits are snapshots: a tag is a convenient way to reference a all content in time of a repository.
See Simon Denier's article "Demystifying Git: 3 Concepts to Understand the Git Model"
Reusing your words : you don't need a tag (or a branch) to have the history of a commit : a commit alone "contains" its history.
One way to visualize this :
start by looking at the history of your master branch, you will see a list of commits with the sha1 of each commit listed :
# --oneline allows to have a shorter description of each commit :
$ git log --oneline master
eacf32b (HEAD, master) newest commit
dd2a663 previous commit
28c9910 yet annother commit
...
now that you have a list of sha1, you can look at the history of any commit using its sha1 :
$ git log --oneline dd2a663
dd2a663 previous commit
28c9910 yet annother commit
...
As you can see : naming the commit is enough to view all of its history.
A tag is just a way to name a commit ; the fact that it contains its history is not a feature of the tag, it is a feature of the commit.
You're starting from a bad place, which is misleading you:
if ... a commit is composed of staged changes ...
Commits are not changes; commits are snapshots.
In some sense, it doesn't really matter much:
git checkout commit
gets you a snapshot, regardless of how they're stored internally.git diff commit1 commit2
shows you a change-set, regardless of how the two commits are stored internally. Running git show commit
just makes Git runs git diff parent-of-commit commit
, so that it shows a change-set.So Git will convert back and forth as needed. But the underlying storage really is snapshots.1
A tag name, like v1.2
, can point directly to a commit, or it can point to an internal tag object, that in turn points to a commit. In either case the tag winds up selecting that commit, which represents the complete snapshot of all files in the state they should be extracted, and you can run:
git checkout v1.2
and get those files into your work-tree. You will have them as a "detached HEAD" rather than being on a branch, but the files will be there.
1Deep below the object-model level, objects get compressed into pack files, and in pack files, they can be delta-compressed. At this point a file can wind up represented as "take some bytes from object A, then some bytes from object B, delete some of the bytes, and insert some other replacements" or whatever it takes to reconstruct the file. These delta chains are not really visible though: Git's visible storage only goes down to the object level. You can do:
git rev-parse a123456:lib/foo.py
and find the object hash ID of a file stored in commit a123456
under the name lib/foo.py
, and you can then extract the bytes of that object with:
git cat-file -p <hash>
and you get the whole file, intact, regardless of whether the object with that hash ID got delta-compressed within a pack file, or is still stored intact as a "loose" object.