What is the Git equivalent for revision number?

2019-01-04 04:45发布

We use SVN at work, but for my personal projects I decided to use Git. So I installed Git yesterday, and I wonder what is the revision number equivalent in Git.

Let's say we work on version 3.0.8 and every bug fix has its own revision number we can use when we talk about this bug fix. So if I tag the code in Git to 3.0.8 what then I can use as a revision number or some other more detailed kind of identification? I find the hash not so user friendly for humans.

18条回答
2楼-- · 2019-01-04 05:31

Post build event for Visual Studio

echo  >RevisionNumber.cs static class Git { public static int RevisionNumber =
git  >>RevisionNumber.cs rev-list --count HEAD
echo >>RevisionNumber.cs ; }
查看更多
唯我独甜
3楼-- · 2019-01-04 05:36

From the Git manual, tags are a brilliant answer to this issue:

Creating an annotated tag in Git is simple. The easiest way is to specify -a when you run the tag command:

$ git tag -a v1.4 -m 'my version 1.4'

$ git tag
v0.1
v1.3
v1.4

Check out 2.6 Git Basics - Tagging

查看更多
劳资没心,怎么记你
4楼-- · 2019-01-04 05:37

Git does not have the same concept of revision numbers as subversion. Instead each given snapshot made with a commit is tagged by a SHA1 checksum. Why? There are several problems with a running revno in a distributed version control system:

First, since development is not linear at all, the attachment of a number is rather hard as a problem to solve in a way which will satisfy your need as a programmer. Trying to fix this by adding a number might quickly become problematic when the number does not behave as you expect.

Second, revision numbers may be generated on different machines. This makes synchronization of numbers much harder - especially since connectivity is one-way; you may not even have access to all machines that has the repository.

Third, in git, somewhat pioneered by the now defunct OpenCM system, the identity of a commit (what the commit is) is equivalent to its name (the SHA id). This naming = identity concept is very strong. When you sit with a commit name in hand it also identifies the commit in an unforgeable way. This in turn lets you check all of your commits back to the first initial one for corruption with the git fsck command.

Now, since we have a DAG (Directed Acyclic Graph) of revisions and these constitute the current tree, we need some tools to solve your problem: How do we discriminate different versions. First, you can omit part of the hash if a given prefix, 1516bd say, uniquely identifies your commit. But this is also rather contrived. Instead, the trick is to use tags and or branches. A tag or branch is akin to a "yellow stick it note" you attach to a given commit SHA1-id. Tags are, in essence, meant to be non-moving whereas a branch will move when new commits are made to its HEAD. There are ways to refer to a commit around a tag or branch, see the man page of git-rev-parse.

Usually, if you need to work on a specific piece of code, that piece is undergoing changes and should as such be a branch with a saying topic name. Creating lots of branches (20-30 per programmer is not unheard of, with some 4-5 published for others to work on) is the trick for effective git. Every piece of work should start as its own branch and then be merged in when it is tested. Unpublished branches can be rewritten entirely and this part of destroying history is a force of git.

When the change is accepted into master it somewhat freezes and becomes archeology. At that point, you can tag it, but more often a reference to the particular commit is made in a bug tracker or issue tracker via the sha1 sum. Tags tend to be reserved for version bumps and branch points for maintenance branches (for old versions).

查看更多
beautiful°
5楼-- · 2019-01-04 05:37

I'd just like to note another possible approach - and that is by using git git-notes(1), in existence since v 1.6.6 (Note to Self - Git) (I'm using git version 1.7.9.5).

Basically, I used git svn to clone an SVN repository with linear history (no standard layout, no branches, no tags), and I wanted to compare revision numbers in the cloned git repository. This git clone doesn't have tags by default, so I cannot use git describe. The strategy here likely would work only for linear history - not sure how it would turn out with merges etc.; but here is the basic strategy:

  • Ask git rev-list for list of all commit history
    • Since rev-list is by default in "reverse chronological order", we'd use its --reverse switch to get list of commits sorted by oldest first
  • Use bash shell to
    • increase a counter variable on each commit as a revision counter,
    • generate and add a "temporary" git note for each commit
  • Then, browse the log by using git log with --notes, which will also dump a commit's note, which in this case would be the "revision number"
  • When done, erase the temporary notes (NB: I'm not sure if these notes are committed or not; they don't really show in git status)

First, let's note that git has a default location of notes - but you can also specify a ref(erence) for notes - which would store them in a different directory under .git; for instance, while in a git repo folder, you can call git notes get-ref to see what directory that will be:

$ git notes get-ref
refs/notes/commits
$ git notes --ref=whatever get-ref
refs/notes/whatever

The thing to be noted is that if you notes add with a --ref, you must also afterwards use that reference again - otherwise you may get errors like "No note found for object XXX...".

For this example, I have chosen to call the ref of the notes "linrev" (for linear revision) - this also means it is not likely the procedure will interfere with already existing notes. I am also using the --git-dir switch, since being a git newbie, I had some problems understanding it - so I'd like to "remember for later" :); and I also use --no-pager to suppress spawning of less when using git log.

So, assuming you're in a directory, with a subfolder myrepo_git which is a git repository; one could do:

### check for already existing notes:

$ git --git-dir=./myrepo_git/.git notes show
# error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33.
$ git --git-dir=./myrepo_git/.git notes --ref=linrev show
# error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33.

### iterate through rev-list three, oldest first,
### create a cmdline adding a revision count as note to each revision

$ ix=0; for ih in $(git --git-dir=./myrepo_git/.git rev-list --reverse HEAD); do \
  TCMD="git --git-dir=./myrepo_git/.git notes --ref linrev"; \
  TCMD="$TCMD add $ih -m \"(r$((++ix)))\""; \
  echo "$TCMD"; \
  eval "$TCMD"; \
done

# git --git-dir=./myrepo_git/.git notes --ref linrev add 6886bbb7be18e63fc4be68ba41917b48f02e09d7 -m "(r1)"
# git --git-dir=./myrepo_git/.git notes --ref linrev add f34910dbeeee33a40806d29dd956062d6ab3ad97 -m "(r2)"
# ...
# git --git-dir=./myrepo_git/.git notes --ref linrev add 04051f98ece25cff67e62d13c548dacbee6c1e33 -m "(r15)"

### check status - adding notes seem to not affect it:

$ cd myrepo_git/
$ git status
# # On branch master
# nothing to commit (working directory clean)
$ cd ../

### check notes again:

$ git --git-dir=./myrepo_git/.git notes show
# error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33.
$ git --git-dir=./myrepo_git/.git notes --ref=linrev show
# (r15)

### note is saved - now let's issue a `git log` command, using a format string and notes:

$ git --git-dir=./myrepo_git/.git --no-pager log --notes=linrev --format=format:"%h: %an: %ad:  >>%s<< %N" HEAD
# 04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000:  >>test message 15 << (r15)
# 77f3902: _user_: Sun Apr 21 18:29:00 2013 +0000:  >>test message 14<< (r14)
# ...
# 6886bbb: _user_: Sun Apr 21 17:11:52 2013 +0000:  >>initial test message 1<< (r1)

### test git log with range:

$ git --git-dir=./myrepo_git/.git --no-pager log --notes=linrev --format=format:"%h: %an: %ad:  >>%s<< %N" HEAD^..HEAD
# 04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000:  >>test message 15 << (r15)

### erase notes - again must iterate through rev-list

$ ix=0; for ih in $(git --git-dir=./myrepo_git/.git rev-list --reverse HEAD); do \
  TCMD="git --git-dir=./myrepo_git/.git notes --ref linrev"; \
  TCMD="$TCMD remove $ih"; \
  echo "$TCMD"; \
  eval "$TCMD"; \
done
# git --git-dir=./myrepo_git/.git notes --ref linrev remove 6886bbb7be18e63fc4be68ba41917b48f02e09d7
# Removing note for object 6886bbb7be18e63fc4be68ba41917b48f02e09d7
# git --git-dir=./myrepo_git/.git notes --ref linrev remove f34910dbeeee33a40806d29dd956062d6ab3ad97
# Removing note for object f34910dbeeee33a40806d29dd956062d6ab3ad97
# ...
# git --git-dir=./myrepo_git/.git notes --ref linrev remove 04051f98ece25cff67e62d13c548dacbee6c1e33
# Removing note for object 04051f98ece25cff67e62d13c548dacbee6c1e33

### check notes again:

$ git --git-dir=./myrepo_git/.git notes show
# error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33.
$ git --git-dir=./myrepo_git/.git notes --ref=linrev show
# error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33.

So, at least in my specific case of fully linear history with no branches, the revision numbers seem to match with this approach - and additionally, it seems that this approach will allow using git log with revision ranges, while still getting the right revision numbers - YMMV with a different context, though...

Hope this helps someone,
Cheers!


EDIT: Ok, here it is a bit easier, with git aliases for the above loops, called setlinrev and unsetlinrev; when in your git repository folder, do (Note the nasty bash escaping, see also #16136745 - Add a Git alias containing a semicolon):

cat >> .git/config <<"EOF"
[alias]
  setlinrev = "!bash -c 'ix=0; for ih in $(git rev-list --reverse HEAD); do \n\
      TCMD=\"git notes --ref linrev\"; \n\
      TCMD=\"$TCMD add $ih -m \\\"(r\\$((++ix)))\\\"\"; \n\
      #echo \"$TCMD\"; \n\
      eval \"$TCMD\"; \n\
    done; \n\
    echo \"Linear revision notes are set.\" '"

  unsetlinrev = "!bash -c 'ix=0; for ih in $(git rev-list --reverse HEAD); do \n\
      TCMD=\"git notes --ref linrev\"; \n\
      TCMD=\"$TCMD remove $ih\"; \n\
      #echo \"$TCMD\"; \n\
      eval \"$TCMD 2>/dev/null\"; \n\
    done; \n\
    echo \"Linear revision notes are unset.\" '"
EOF

... so you can simply invoke git setlinrev before trying to do log involving linear revision notes; and git unsetlinrev to delete those notes when you're done; an example from inside the git repo directory:

$ git log --notes=linrev --format=format:"%h: %an: %ad:  >>%s<< %N" HEAD^..HEAD
04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000:  >>test message 15 <<

$ git setlinrev
Linear revision notes are set.
$ git log --notes=linrev --format=format:"%h: %an: %ad:  >>%s<< %N" HEAD^..HEAD
04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000:  >>test message 15 << (r15)
$ git unsetlinrev
Linear revision notes are unset.

$ git log --notes=linrev --format=format:"%h: %an: %ad:  >>%s<< %N" HEAD^..HEAD
04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000:  >>test message 15 <<

The time it would take the shell to complete these aliases, would depend on the size of the repository history.

查看更多
聊天终结者
6楼-- · 2019-01-04 05:38

The SHA1 hash of the commit is the equivalent to a Subversion revision number.

查看更多
我想做一个坏孩纸
7楼-- · 2019-01-04 05:38

After researching in the online repositories for a long time I found that logically there is no difference between a revision number and a commit number in Git.

查看更多
登录 后发表回答