-1

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 ?

olleo
  • 378
  • 3
  • 14
  • A tag is nothing more than a pointer to a commit, as you noted. It has that commit's history as a result. I'm not entirely sure what your question is, as it's a straightforward logical proof. – jhpratt Sep 05 '19 at 02:06
  • @jhpratt if i create a tag using a specific commit. will this tag contains all the commits (changes) before the specific commit ? How it works internally ? – olleo Sep 05 '19 at 02:22
  • The **tag** doesn't, the **commit** does. If you're solely asking about the internals, that is far too broad of a question. I suggest you search a bit instead. – jhpratt Sep 05 '19 at 02:23
  • 2
    Commits are not changes; commits are snapshots. It doesn't really matter much since what you do *with* a commit is to extract files (`git checkout `) or compare files to some other commit (`git diff `) or the like, and Git will convert the snapshot *to* changes, via `git diff`, when necessary. – torek Sep 05 '19 at 05:07
  • @torek, please post your comment as an answer. By saying "commits are not changes but snapshots", you've just changed my point of view. I though, it was changes since we commit "changed staged files". But if they are snapshots, then everything comes in place. – olleo Sep 06 '19 at 23:53

3 Answers3

2

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.

  • a lightweight tag is just a direct reference to a commit SHA1 (and is supposed to remain local, not to be pushed)
  • an annotated tag is a regular Git object (with an author and date: it can be pushed), and is a reference to 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"

https://sogilis.com/wp-content/uploads/2015/05/changeset.png

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
1

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.

LeGEC
  • 46,477
  • 5
  • 57
  • 104
0

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:

  • Running git checkout commit gets you a snapshot, regardless of how they're stored internally.
  • Running, e.g., 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.

torek
  • 448,244
  • 59
  • 642
  • 775