How do I get the hash of the current commit in Git?
Mateen Ulhaq
23.8k18 gold badges95 silver badges132 bronze badges
asked Jun 4, 2009 at 8:42
2
To turn any extended object reference into a hash, use git-rev-parse
:
git rev-parse HEAD
or
git rev-parse --verify HEAD
To retrieve the short hash:
git rev-parse --short HEAD
To turn references (e.g. branches and tags) into hashes, use git show-ref
and git for-each-ref
.
Mateen Ulhaq
23.8k18 gold badges95 silver badges132 bronze badges
answered Jun 4, 2009 at 9:05
Jakub NarębskiJakub Narębski
306k65 gold badges214 silver badges230 bronze badges
20
To get the shortened commit hash, use the %h
format specifier:
git log --pretty=format:'%h' -n 1
%H
represents the long commit hash. Also, -1
can be used directly in place of -n 1
.
Mateen Ulhaq
23.8k18 gold badges95 silver badges132 bronze badges
answered Sep 30, 2011 at 23:32
outofcultureoutofculture
5,9631 gold badge17 silver badges20 bronze badges
10
Another one, using git log:
git log -1 --format="%H"
It’s very similar to the of @outofculture though a bit shorter.
answered Nov 21, 2011 at 18:48
Paul PladijsPaul Pladijs
18.2k5 gold badges28 silver badges31 bronze badges
6
To get the full SHA:
$ git rev-parse HEAD
cbf1b9a1be984a9f61b79a05f23b19f66d533537
To get the shortened version:
$ git rev-parse --short HEAD
cbf1b9a
answered Jul 29, 2016 at 20:04
AlexanderAlexander
104k30 gold badges199 silver badges194 bronze badges
1
Commit hash
git show -s --format=%H
Abbreviated commit hash
git show -s --format=%h
The -s
flag is same as --no-patch
and stands for «Suppress diff output».
Click here for more git show
examples.
Niko Pasanen
26.9k10 gold badges92 silver badges96 bronze badges
answered Mar 27, 2017 at 19:33
ecwpz91ecwpz91
1,50713 silver badges8 bronze badges
For completeness, since no one has suggested it yet. .git/refs/heads/master
is a file that contains only one line: the hash of the latest commit on master
. So you could just read it from there.
Or, as a command:
cat .git/refs/heads/master
Update:
Note that git now supports storing some head refs in the pack-ref file instead of as a file in the /refs/heads/ folder. https://www.kernel.org/pub/software/scm/git/docs/git-pack-refs.html
Pang
9,481146 gold badges81 silver badges122 bronze badges
answered Oct 16, 2012 at 11:34
DeestanDeestan
16.7k4 gold badges32 silver badges48 bronze badges
11
There’s always git describe
as well. By default it gives you —
john@eleanor:/dev/shm/mpd/ncmpc/pkg (master)$ git describe --always
release-0.19-11-g7a68a75
answered Aug 26, 2011 at 10:43
John TyreeJohn Tyree
1,0607 silver badges14 bronze badges
4
Use git rev-list --max-count=1 HEAD
answered Jun 4, 2009 at 8:48
Robert MunteanuRobert Munteanu
66.7k35 gold badges205 silver badges277 bronze badges
1
If you need to store the hash in a variable during a script, you can use
last_commit=$(git rev-parse HEAD);
Or, if you only want the first 10 characters (like github.com does)
last_commit=$(git rev-parse --short=10 HEAD);
nik7
8073 gold badges11 silver badges20 bronze badges
answered Jul 15, 2012 at 16:04
HenkHenk
5464 silver badges5 bronze badges
1
If you want the super-hacky way to do it:
cat .git/`cat .git/HEAD | cut -d -f 2`
Basically, git stores the location of HEAD in .git/HEAD, in the form ref: {path from .git}
. This command reads that out, slices off the «ref: «, and reads out whatever file it pointed to.
This, of course, will fail in detached-head mode, as HEAD won’t be «ref:…», but the hash itself — but you know, I don’t think you expect that much smarts in your bash one-liners. If you don’t think semicolons are cheating, though…
HASH="ref: HEAD"; while [[ $HASH == ref:* ]]; do HASH="$(cat ".git/$(echo $HASH | cut -d -f 2)")"; done; echo $HASH
dave1010
15.1k7 gold badges67 silver badges64 bronze badges
answered Oct 14, 2015 at 19:15
FordiFordi
2,74824 silver badges20 bronze badges
4
I needed something a little more different: display the full sha1 of the commit, but append an asterisk to the end if the working directory is not clean. Unless I wanted to use multiple commands, none of the options in the previous answers work.
Here is the one liner that does:
git describe --always --abbrev=0 --match "NOT A TAG" --dirty="*"
Result: f5366ccb21588c0d7a5f7d9fa1d3f85e9f9d1ffe*
Explanation: describes (using annotated tags) the current commit, but only with tags containing «NOT A TAG». Since tags cannot have spaces, this never matches a tag and since we want to show a result --always
, the command falls back displaying the full (--abbrev=0
) sha1 of the commit and it appends an asterisk if the working directory is --dirty
.
If you don’t want to append the asterisk, this works like all the other commands in the previous answers:
git describe --always --abbrev=0 --match "NOT A TAG"
Result: f5366ccb21588c0d7a5f7d9fa1d3f85e9f9d1ffe
answered Feb 17, 2018 at 19:06
RadoRado
8,5547 gold badges30 silver badges44 bronze badges
3
git rev-parse HEAD
does the trick.
If you need to store it to checkout back later than saving actual branch if any may be preferable:
cat .git/HEAD
Example output:
ref: refs/heads/master
Parse it:
cat .git/HEAD | sed "s/^.+ (.+)$/1/g"
If you have Windows then you may consider using wsl.exe:
wsl cat .git/HEAD | wsl sed "s/^.+ (.+)$/1/g"
Output:
refs/heads/master
This value may be used to git checkout later but it becomes pointing to its SHA. To make it to point to the actual current branch by its name do:
wsl cat .git/HEAD | wsl sed "s/^.+ (.+)$/1/g" | wsl sed "s/^refs///g" | wsl sed "s/^heads///g"
Output:
master
sdgfsdh
32.8k26 gold badges128 silver badges235 bronze badges
answered Nov 4, 2019 at 16:12
Sergei KrivonosSergei Krivonos
4,1513 gold badges39 silver badges54 bronze badges
The most succinct way I know:
git show --pretty=%h
If you want a specific number of digits of the hash you can add:
--abbrev=n
answered Feb 7, 2014 at 6:43
Brian PetersonBrian Peterson
3,0136 gold badges29 silver badges35 bronze badges
3
Perhaps you want an alias so you don’t have to remember all the nifty details. After doing one of the below steps, you will be able to simply type:
$ git lastcommit
49c03fc679ab11534e1b4b35687b1225c365c630
Following up on the accepted answer, here are two ways to set this up:
1) Teach git the explicit way by editing the global config (my original answer):
# open the git config editor
$ git config --global --edit
# in the alias section, add
...
[alias]
lastcommit = rev-parse HEAD
...
2) Or if you like a shortcut to teach git a shortcut, as recently commented by Adrien:
$ git config --global alias.lastcommit "rev-parse HEAD"
From here on, use git lastcommit
to show the last commit’s hash.
answered Oct 9, 2015 at 12:53
miraculixxmiraculixx
9,9842 gold badges40 silver badges60 bronze badges
0
Here is one-liner in Bash shell using direct read from git files:
(head=($(<.git/HEAD)); cat .git/${head[1]})
You need to run above command in your git root folder.
This method can be useful when you’ve repository files, but git
command has been not installed.
If won’t work, check in .git/refs/heads
folder what kind of heads do you have present.
answered Oct 5, 2017 at 22:11
kenorbkenorb
153k85 gold badges674 silver badges738 bronze badges
git show-ref --head --hash head
If you’re going for speed though, the approach mentioned by Deestan
cat .git/refs/heads/<branch-name>
is significantly faster than any other method listed here so far.
answered Apr 30, 2015 at 7:06
DennisDennis
56.1k25 gold badges142 silver badges139 bronze badges
1
in your home-dir in file «.gitconfig» add the following
[alias]
sha = rev-parse HEAD
then you will have an easier command to remember:
$ git sha
59fbfdbadb43ad0b6154c982c997041e9e53b600
answered Feb 18, 2016 at 16:16
jo_jo_
7,8991 gold badge18 silver badges14 bronze badges
On git bash, simply run
$ git log -1
you will see, these lines following your command.
commit d25c95d88a5e8b7e15ba6c925a1631a5357095db .. (info about your head)
d25c95d88a5e8b7e15ba6c925a1631a5357095db, is your SHA for last commit.
answered Nov 26, 2019 at 12:40
BattyBatty
1211 silver badge5 bronze badges
Pretty print of main git repo, and sub-modules:
echo "Main GIT repo:"
echo $(git show -s --format=%H) '(main)'
echo "Sub-modules:"
git submodule status | awk '{print $1,$2}'
Example output:
3a032b0992d7786b00a8822bbcbf192326160cf9 (main)
7de695d58f427c0887b094271ba1ae77a439084f sub-module-1
58f427c0887b01ba1ae77a439084947de695d27f sub-module-2
d58f427c0887de6957b09439084f4271ba1ae77a sub-module-3
answered Feb 27, 2022 at 8:52
leenremmleenremm
97312 silver badges17 bronze badges
How I would do it in python (based on @kenorb’s bash answer)
def get_git_sha():
# Which branch are we on?
branch = open(".git/HEAD", "r").read()
# Parse output "ref: refs/heads/my_branch" -> my_branch
branch = branch.strip().split("/")[-1]
# What's the latest commit in this branch?
return open(f".git/refs/heads/{branch}").read().strip()
answered Mar 22, 2022 at 15:24
ignorantignorant
1,3801 gold badge10 silver badges14 bronze badges
Here is another direct-access implementation:
head="$(cat ".git/HEAD")"
while [ "$head" != "${head#ref: }" ]; do
head="$(cat ".git/${head#ref: }")"
done
This also works over http which is useful for local package archives (I know: for public web sites it’s not recommended to make the .git directory accessable):
head="$(curl -s "$baseurl/.git/HEAD")"
while [ "$head" != "${head#ref: }" ]; do
head="$(curl -s "$baseurl/.git/${head#ref: }")"
done
answered Nov 4, 2016 at 13:20
Daniel AlderDaniel Alder
4,9802 gold badges45 silver badges54 bronze badges
Here is another way of doing it with
git log | grep -o 'w{8,}' | head -n 1
answered Feb 10, 2017 at 15:14
Marcelo LazaroniMarcelo Lazaroni
9,5993 gold badges34 silver badges41 bronze badges
echo «printing last commit id# for current branch:»;
git reflog
answered Feb 23, 2022 at 10:53
1
I wanted the newest commit on the origin/main
branch so I use
git ls-remote origin | grep main$ | cut -f 1
answered Mar 15 at 19:49
mvndaaimvndaai
3,3132 gold badges28 silver badges34 bronze badges
Viewing the Commit History
After you have created several commits, or if you have cloned a repository with an existing commit history, you’ll probably want to look back to see what has happened.
The most basic and powerful tool to do this is the git log
command.
These examples use a very simple project called “simplegit”.
To get the project, run:
$ git clone https://github.com/schacon/simplegit-progit
When you run git log
in this project, you should get output that looks something like this:
$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
Change version number
commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sat Mar 15 16:40:33 2008 -0700
Remove unnecessary test
commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sat Mar 15 10:31:28 2008 -0700
Initial commit
By default, with no arguments, git log
lists the commits made in that repository in reverse chronological order; that is, the most recent commits show up first.
As you can see, this command lists each commit with its SHA-1 checksum, the author’s name and email, the date written, and the commit message.
A huge number and variety of options to the git log
command are available to show you exactly what you’re looking for.
Here, we’ll show you some of the most popular.
One of the more helpful options is -p
or --patch
, which shows the difference (the patch output) introduced in each commit.
You can also limit the number of log entries displayed, such as using -2
to show only the last two entries.
$ git log -p -2
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
Change version number
diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,7 +5,7 @@ require 'rake/gempackagetask'
spec = Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = "simplegit"
- s.version = "0.1.0"
+ s.version = "0.1.1"
s.author = "Scott Chacon"
s.email = "schacon@gee-mail.com"
s.summary = "A simple gem for using Git in Ruby code."
commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sat Mar 15 16:40:33 2008 -0700
Remove unnecessary test
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index a0a60ae..47c6340 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -18,8 +18,3 @@ class SimpleGit
end
end
-
-if $0 == __FILE__
- git = SimpleGit.new
- puts git.show
-end
This option displays the same information but with a diff directly following each entry.
This is very helpful for code review or to quickly browse what happened during a series of commits that a collaborator has added.
You can also use a series of summarizing options with git log
.
For example, if you want to see some abbreviated stats for each commit, you can use the --stat
option:
$ git log --stat
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
Change version number
Rakefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sat Mar 15 16:40:33 2008 -0700
Remove unnecessary test
lib/simplegit.rb | 5 -----
1 file changed, 5 deletions(-)
commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sat Mar 15 10:31:28 2008 -0700
Initial commit
README | 6 ++++++
Rakefile | 23 +++++++++++++++++++++++
lib/simplegit.rb | 25 +++++++++++++++++++++++++
3 files changed, 54 insertions(+)
As you can see, the --stat
option prints below each commit entry a list of modified files, how many files were changed, and how many lines in those files were added and removed.
It also puts a summary of the information at the end.
Another really useful option is --pretty
.
This option changes the log output to formats other than the default.
A few prebuilt option values are available for you to use.
The oneline
value for this option prints each commit on a single line, which is useful if you’re looking at a lot of commits.
In addition, the short
, full
, and fuller
values show the output in roughly the same format but with less or more information, respectively:
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 Change version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Remove unnecessary test
a11bef06a3f659402fe7563abf99ad00de2209e6 Initial commit
The most interesting option value is format
, which allows you to specify your own log output format.
This is especially useful when you’re generating output for machine parsing — because you specify the format explicitly, you know it won’t change with updates to Git:
$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 6 years ago : Change version number
085bb3b - Scott Chacon, 6 years ago : Remove unnecessary test
a11bef0 - Scott Chacon, 6 years ago : Initial commit
Specifier | Description of Output |
---|---|
|
Commit hash |
|
Abbreviated commit hash |
|
Tree hash |
|
Abbreviated tree hash |
|
Parent hashes |
|
Abbreviated parent hashes |
|
Author name |
|
Author email |
|
Author date (format respects the |
|
Author date, relative |
|
Committer name |
|
Committer email |
|
Committer date |
|
Committer date, relative |
|
Subject |
You may be wondering what the difference is between author and committer.
The author is the person who originally wrote the work, whereas the committer is the person who last applied the work.
So, if you send in a patch to a project and one of the core members applies the patch, both of you get credit — you as the author, and the core member as the committer.
We’ll cover this distinction a bit more in Distributed Git.
The oneline
and format
option values are particularly useful with another log
option called --graph
.
This option adds a nice little ASCII graph showing your branch and merge history:
$ git log --pretty=format:"%h %s" --graph
* 2d3acf9 Ignore errors from SIGCHLD on trap
* 5e3ee11 Merge branch 'master' of https://github.com/dustin/grit.git
|
| * 420eac9 Add method for getting the current branch
* | 30e367c Timeout code and tests
* | 5a09431 Add timeout protection to grit
* | e1193f8 Support for heads with slashes in them
|/
* d6016bc Require time for xmlschema
* 11d191e Merge branch 'defunkt' into local
This type of output will become more interesting as we go through branching and merging in the next chapter.
Those are only some simple output-formatting options to git log
— there are many more.
Common options to git log
lists the options we’ve covered so far, as well as some other common formatting options that may be useful, along with how they change the output of the log
command.
Option | Description |
---|---|
|
Show the patch introduced with each commit. |
|
Show statistics for files modified in each commit. |
|
Display only the changed/insertions/deletions line from the |
|
Show the list of files modified after the commit information. |
|
Show the list of files affected with added/modified/deleted information as well. |
|
Show only the first few characters of the SHA-1 checksum instead of all 40. |
|
Display the date in a relative format (for example, “2 weeks ago”) instead of using the full date format. |
|
Display an ASCII graph of the branch and merge history beside the log output. |
|
Show commits in an alternate format. Option values include |
|
Shorthand for |
Limiting Log Output
In addition to output-formatting options, git log
takes a number of useful limiting options; that is, options that let you show only a subset of commits.
You’ve seen one such option already — the -2
option, which displays only the last two commits.
In fact, you can do -<n>
, where n
is any integer to show the last n
commits.
In reality, you’re unlikely to use that often, because Git by default pipes all output through a pager so you see only one page of log output at a time.
However, the time-limiting options such as --since
and --until
are very useful.
For example, this command gets the list of commits made in the last two weeks:
$ git log --since=2.weeks
This command works with lots of formats — you can specify a specific date like "2008-01-15"
, or a relative date such as "2 years 1 day 3 minutes ago"
.
You can also filter the list to commits that match some search criteria.
The --author
option allows you to filter on a specific author, and the --grep
option lets you search for keywords in the commit messages.
Note |
You can specify more than one instance of both the |
Another really helpful filter is the -S
option (colloquially referred to as Git’s “pickaxe” option), which takes a string and shows only those commits that changed the number of occurrences of that string.
For instance, if you wanted to find the last commit that added or removed a reference to a specific function, you could call:
$ git log -S function_name
The last really useful option to pass to git log
as a filter is a path.
If you specify a directory or file name, you can limit the log output to commits that introduced a change to those files.
This is always the last option and is generally preceded by double dashes (--
) to separate the paths from the options:
$ git log -- path/to/file
Option | Description |
---|---|
|
Show only the last n commits. |
|
Limit the commits to those made after the specified date. |
|
Limit the commits to those made before the specified date. |
|
Only show commits in which the author entry matches the specified string. |
|
Only show commits in which the committer entry matches the specified string. |
|
Only show commits with a commit message containing the string. |
|
Only show commits adding or removing code matching the string. |
For example, if you want to see which commits modifying test files in the Git source code history were committed by Junio Hamano in the month of October 2008 and are not merge commits, you can run something like this:
$ git log --pretty="%h - %s" --author='Junio C Hamano' --since="2008-10-01"
--before="2008-11-01" --no-merges -- t/
5610e3b - Fix testcase failure when extended attributes are in use
acd3b9e - Enhance hold_lock_file_for_{update,append}() API
f563754 - demonstrate breakage of detached checkout with symbolic link HEAD
d1a43f2 - reset --hard/read-tree --reset -u: remove unmerged new paths
51a94af - Fix "checkout --track -b newbranch" on detached HEAD
b0ad11e - pull: allow "git pull origin $something:$current_branch" into an unborn branch
Of the nearly 40,000 commits in the Git source code history, this command shows the 6 that match those criteria.
Tip |
Preventing the display of merge commits Depending on the workflow used in your repository, it’s possible that a sizable percentage of the commits in your log history are just merge commits, which typically aren’t very informative. |
Урок, в котором мы подробнее рассмотрим историю коммитов и научимся путешествовать по истории
Видеоурок
Конспект урока
Краткое содержание урока, основные инструкции для командной строки, полезные ссылки и советы.
Для информации
Урок частично повторяет содержание предыдущего. Но в отличие от прошлого историю коммитов мы рассмотрим намного подробнее.
История коммитов
Сохранение истории изменений или история коммитов — одна из самых важных частей git. В истории сохраняются все коммиты, по которым можно посмотреть автора коммита, commit message, дату коммита и его хэш.
А также можно увидеть измененные файлы и изменения в каждом файле. То есть git хранит буквально все, от самого начала проекта.
Команда git log
За просмотр истории коммитов отвечает команда git log. В сочетании с различными параметрами эта команда выводит историю по-разному.
Есть много различных вариантов и комбинаций параметров, посмотрим некоторые из них
git log, просмотр истории по умолчанию
$ git log
Показывает все коммиты от новых к старым. Для каждого коммита выводится
- хэш
- автор
- дата
- сообщение (commit message)
git log -p, расширенный вывод истории
$ git log -p
Выводит то же, что и git log, но еще и с изменениями в файлах
git log —oneline, короткая запись
$ git log --oneline
Вывод коммитов в одну строку. Показывает только хэш коммита и commit message
git log —stat —graph, история в виде дерева
$ git log --stat --graph
Выводит коммиты в виде дерева, в командной строке псевдографикой. Плюс выводит список измененных файлов. К дереву коммитов мы вернемся, когда будем работать с ветками.
Сортировка и фильтрация истории
Есть множество команд, которые позволяют сортировать и фильтровать историю коммитов в командной строке. В том числе в сочетании с линуксовыми командами.
Рассмотрим некоторые из них
Поиск по коммитам
Команда grep — мощный инструмент, который помогает работать в том числе и с git. Например, искать по коммитам
git log --oneline | grep revert # поиск упоминания revert
git log --oneline | grep -i revert # независимо от регистра
Коммиты, затронувшие один файл
git log index.html
Поиск по автору
git log --author webdevkin
В опции —author можно указать имя или email, необязательно целиком, можно только часть.
Поиск по диапазону дат
Опции —after и —before задают начальную и конечную даты коммитов
git log --after='2020-03-09 15:30' --before='2020-03-09 16:00'
Комбинация команд и опций
Команды и опции git можно комбинировать и дополнять их линуксовыми командами
git log --author=webdevkin --oneline | grep footer # все коммиты от автора, в которых упоминается footer
git log --oneline | wc -l # количество коммитов
Какие еще есть варианты
Мы рассмотрели базовые примеры, но в документации по git log есть много различных опций.
Все их рассматривать нет смысла, при необходимости изучайте документацию.
git log --help
Просмотр отдельного коммита, git show
Чтобы просмотреть отдельный коммит, нужно узнать его хэш. Хэш коммита выводится в любой команде git log, с параметрами или без. Например,
$ git log --oneline
7b7d7fa Fixed footer
26812f9 Revert "Fixed footer"
0f90ae7 Revert "Fixed styles"
...
a1f3c45 Added footer
a65aa43 Added new block students to main page
0b90433 Initial commit
Смотрим второй коммит
$ git show 43f6afc
Выводится подробная информация о коммите:
- хэш
- автор
- дата
- commit message
- список измененных файлов
- изменения в каждом файле
Короткий хэш коммита
Хэш коммита 40-символьный, но можно использовать короткую запись — первые 7 символов хэша.
Команда git log —oneline выводит именно короткий хэш. Для других операций с коммитами достаточно использовать первые 4 символа.
Например, 3 команды ниже покажут содержимое одного и того же коммита
$ git show 051f75475cb1dca3cd08c1c7367a3308671ccf7b
$ git show 051f754
$ git show 051f
История коммитов в PhpStorm
В окне Local Changes, на вкладке Log показывается вся история коммитов, в левой половине вкладки. В списке коммитов показываются их commit message, автор и дата.
Клик на нужный коммит откроет в правой части вкладки список измененных файлов. Клик на нужном файле и Ctrl/Cmd+D покажет все изменения в этом файле точно так же, как и git diff.
В тексте объяснить работу с историей в PhpStorm сложно, смотрите видеоурок.
Переключение на старый коммит, зачем это нужно
Нужно это обычно в двух случаях:
1. При неудачном деплое, когда вскрылась критичная бага.
Если бага сложная и пофиксить ее быстро не удается, можно откатиться на рабочий коммит, задеплоить рабочую версию и уже потом чинить багу.
2. При отладке. Когда в код закралась бага и мы постепенно продвигаемся «назад в прошлое» и ищем, в какой момент что-то сломалось
Как переключиться на коммит в терминале
Первое — узнать хэш нужного коммита. Например, имеем такую историю
$ git log --oneline
7b7d7fa Fixed footer
26812f9 Revert "Fixed footer"
0f90ae7 Revert "Fixed styles"
...
a1f3c45 Added footer
a65aa43 Added new block students to main page
0b90433 Initial commit
Хотим переключиться на предпоследний коммит. Коммиты идут в порядке убывания, поэтому нам нужен второй сверху — 26812f9. Переключаемся на него командой
$ git checkout 26812f9
Все, вернулись в прошлое. Проверим историю, теперь коммит, на который мы переключились — последний
$ git log --oneline
26812f9 Revert "Fixed footer"
0f90ae7 Revert "Fixed styles"
...
a1f3c45 Added footer
a65aa43 Added new block students to main page
0b90433 Initial commit
Уйдем еще дальше, переключимся на первый коммит. Так как коммиты упорядочиваются по убыванию даты, то первый коммит — это последний в списке — 0b90433 Initial commit
$ git checkout 0b90433
Проверяем историю
$ git log --oneline
0b90433 Initial commit
Чтобы вернуться обрано, в исходное состояние, нужно набрать
$ git checkout master
master — это ветка, в которой мы работаем по умолчанию. О ветках поговорим через пару уроков
Как переключаться между коммитами в PhpStrom
Вкладка Log, правый клик на нужном коммите и Checkout Revision. Все. История коммитов будет видна по-прежнему вся, но напротив текущего коммита будет стоять значок HEAD с символом «!»
Как вернуться обратно? В правом нижем угле PhpStorm есть пункт git:, кликаем на него, выбираем Local Branches — master — checkout. Значок «!» пропадет — мы вернулись в исходное состояние
Что могу посоветовать
- как и git diff, историю коммитов git log удобнее смотреть в PhpStorm
- в команде git log есть множество вариантов сортировки и фильтрации
- сочетание git log с простыми линуксовыми командами дает хороший эффект. Обычный grep — очень хороший помощник
- PhpStorm предоставляет удобные возможности по фильтрации коммитов. Можно искать коммиты по commit message, по автору, дате и по папкам, в которых происходили изменения
- перемещайтесь по истории осторожно, не забывайте возвращаться в исходное состояние
На этом все. В следующем уроке мы поговорим о взаимодействии с сервером и познакомимся с командами git push и git pull.
Спасибо за внимание и до встречи!
Все уроки курса
- Вводный урок
- 1. Установка и базовая настройка git
- 2. Создание и клонирование репозитория git
- 3. Делаем первые изменения, git status и git diff
- 4. Коммиты и история коммитов, git commit, git log и git show
- 5. Подробнее об истории коммитов. Путешествие по истории
- 6. Работа с сервером, git push и git pull
- 7. Ветки — главная фишка git, git branch и git checkout
- 8. Работа с ветками на сервере, git fetch
- 9. Слияния или мерджи веток, git merge
- 10. Конфликты и их разрешение
- Платная часть курса. Презентация
- * 11. Работа с gitignore и git exclude
- * 12. Буфер обмена git, git stash
- * 13. Копирование коммитов, git cherry-pick
- * 14. Отмена и редактирование последнего коммита
- * 15. Отмена произвольного коммита, git revert
- 16. Склеивание коммитов, git rebase —interactive и git reflog
- * 17. Зачем склеивать коммиты. Плюсы и минусы сквоша
- * 18. Работа с git rebase. Отличия от merge
- * 19. Что такое git push —force и как с ним работать
- * 20. Ищем баги с помощью git, git bisect
- * 21. Как и зачем работать с тегами git
- * 22. Процессы: github flow и git flow
- * 23. Псевдонимы в git
- 24. Мердж-реквесты
- * 25. Форки
* платные уроки
список обновляется…
The git log command is used for listing and filtering the project history and searching for particular changes. Let’s figure out how you can retrieve hash for commits below.
You can use git log -1 to show the information about the latest commit, and from that information, you can get the commit hash by using the --format option as shown below:
git log -1 --format=format:"%H" # 2701112aee3d8a7ba13099b1aefec53e1fd3ca3a
Here, %H means commit hash.
As an alternative, you can use the git-rev-parse command, which will return the hash of the latest git commit:
git rev-parse HEAD
# 2701112aee3d8a7ba13099b1aefec53e1fd3ca3a
If you want to turn references (branches and tags) into hash, you can use git show-ref and git for-each-ref commands.
In case you want to get only the first 8 digits, you can use the cut -c 1-8 filter in the following way:
git rev-parse HEAD | cut -c 1-8
# 2701112a
Using the git reflog command is also used if you want to have the history on the head of your branches. With this command, you can find the line referring to the state you want to get back. After getting the hash of the commit you can restore it by using git cherry-pick.
The git log command displays the committed snapshots. It only works on the committed history, whereas git status controls the working directory and the staging area.
The git log command examines a repository’s history and finds a particular version of a project. Log output can be personalized differently, from filtering commits to displaying them in an entirely user-defined format.
The --format option pretty-prints the contents of the commit logs in a specified format, where <format> can be oneline, short, medium, eference, email, full, fuller, raw, format:<string> and tformat:<string>. When <format> is not specified and has %placeholder, it will act as if --pretty=tformat:<format> were specified.
I’m not going to edit your question since I cannot be sure that this is your intent, but it sounds like your real question comes down to this:
Suppose I have a directory full of source code. Suppose further that I believe this directory-of-source-code was created by running a series of
git
commands, such as:git checkout [some hash ID] git cherry-pick [another hash ID]
but Evil Spirits, or fluoridation, or some such, has lost the
.git
directory. So all I have now is this tree of code. Let’s call this «my lost tree».Meanwhile, on some other machine or in some other directory, I do have a full Git repository. I am curious as to what commit or commits (there may be more than one) would, if I ran
git checkout <hash>
, get me a work-tree that is identical to my lost tree.
Now, it’s not clear what the point of all this is, but it is possible to do, with some caveats. The easy way to do it is to add the lost tree to the full Git repository—or, if you are concerned about precious bodily fluids (see the «fluoridation» link above), to a git clone --mirror
of it. (The clone is «as good as» the original, but can be thrown away after this process.)
Things to know before steps 1 and 2
In any normal Git repository, there are three things of interest while you are working on making a new commit:
- the current commit, which is your
HEAD
; - the index, which is where you build the new commit; and
- the work-tree, which is where you keep files in a form the rest of the computer can deal with, as the files stored in the current commit and in the index are in a form only Git can deal with.
As I mentioned in a comment, the repository database itself has four object types: blob
(a file); tag
(an annotated tag: a human-readable name for some commit object, plus some other metadata); commit
(metadata including a log message, an author and committer, the parent commit(s) IDs of the commit—it’s these parent IDs that produce the history, when commits are viewed according to parent/child relationships); and tree
. A tree
object contains the names and hash-IDs of blobs and of additional sub-trees, and hence could represent a tree of files identical to your lost tree. Each commit has, as part of its metadata, the hash ID of the commit’s stored snapshot, i.e., the tree. So if your lost tree is in fact in the repository, its hash ID is stored in some commit(s).
We will make use of the fact that, once some object is in the repository, any attempt to put a bit-for-bit-identical new object into the repository simply re-uses the existing object. Making one grand assumption,1 this re-use is fundamentally safe: if the new object is bit-for-bit identical to the old object, why would you care which object—new or old—Git pulls out later when you ask Git to retrieve the object by its hash ID?
1The assumption is that no two different objects produce the same hash ID, ever. The pigeonhole principle tells us that this assumption is false in theory, but in practice, it’s actually true. It is possible, but currently very expensive, to break the assumption deliberately. A longer hash ID can—again, in theory at least—be less-breakable, although cryptography is always getting weirder.
In fact, if two different Git objects do produce the same hash, Git breaks … well, sort of; it breaks, or should break, in a «safe» manner. The example PDFs that break SHA-1 do not actually break Git, though. Other files could—but meanwhile, some minor coding glitches in existing versions of Git apparently cause it to fail to alert the user to the fact that it will fail to store some new version in your repository.
Step 1: Finding the hash ID of your lost tree
The work-tree corresponds to your lost tree, but Git won’t make a tree object out of a work-tree. Git will only make a tree object out of the index. This means that in order to find the hash ID of your lost tree, you must copy it into the index.
The index already has stuff in it, so your first step is to remove all of it. In the top level of your Git repository, tell Git to remove everything, both from the work-tree and from the index:
git rm -rf .
You should now have an empty work-tree … unless—this is one of the caveats—there are untracked files.2 If there are some untracked files, you will have to find out (or guess) whether those are also in your lost tree, and whether they will also be untracked in the commit(s) in the full repository that use that same tree. I leave it to you to find solutions to this problem, should you actually have this problem. (It’s possible that even if there are untracked files in the repository, they were and are not present in your lost tree.)
In any case, you probably want to discard any untracked files at this point. If there are any, you can use git clean -fdx
to discard them. (This can be a good reason to work on a fresh mirror clone: it won’t have any such untracked files in the first place, and removing such files from a real work-tree may force you to rebuild them later, which for a large project, might be many CPU-hours of computation.)
Now that your Git index and work-tree are empty, we will re-fill them from the lost tree:
(cd /path/to/lost/tree; tar cf - .) | tar xf -
or:
cp -R /path/to/lost/tree .
or whatever, so that the work-tree is now a copy of the lost tree.
(At this point, you must throw out, from the copy, any files that should be untracked. Since we removed everything, we also removed any .gitignore
files that we had before, so files that would be ignored, if this were a normal setup, won’t be, unless those .gitignore
files are in your lost tree. Again, how you do this, if you need to do it at all, is up to you.)
Second-to-last, we now want to populate our index from this work-tree. This part is simple:
git add .
does the trick. We now have a full index and can produce a tree object and find its hash.
The «normal» way to do this is to make a new commit. If we make a new commit now, it will have as its parent, our current (HEAD
) commit. It will be added to our current branch. There’s nothing wrong with this, but that’s not our goal at this point, so we can use a lower-level Git command, one of the so-called plumbing commands:
git write-tree
What this does is turn the index into a series of tree objects, one for each sub-directory (and that sub-directory’s files) stored in the index, and one final top-level tree, for the files and sub-directories at the top level, i.e., for .
. The output is the hash ID of the object just added to—or reused from—the existing Git object database:
$ git write-tree
b3bb4696cf8dcb93c1f09a447f6b7356bccb24d2
This tree hash is what we are looking for, but it’s not a commit hash. We simply believe that there may be one, two, or many existing commits that have this tree as their hash.3
2An untracked file is simply one that is not in the index. This simple definition is not a problem for us unless and until it becomes a very big problem: if your lost tree contains untracked files, you don’t know which ones are untracked because the index that made them untracked was part of the Git repository you lost when you acquired the lost tree in the first place!
3If we use git commit
to make a new commit, the new commit we just made will have this hash as its tree
object. That’s not the commit we’re looking for, of course—but if you use git commit
instead of git write-tree
, this is something to keep in mind.
Step 2: finding commits that have this tree
The remaining caveat, of course, is that it’s quite possible that no commit has the tree your just made; or it may be in two or more commits. The latter occurs from time to time due to git revert
or trivial merges (merges that could be, but on purpose are not, fast-forwards). The way to deal with this is to find all such commits, then decide which one(s) you want.
To find these commits, our first sub-step is to enumerate every commit in the repository. We need their hash IDs, so that we can use another Git plumbing command to find their tree ID (remember, each commit has exactly one tree). The command to find every commit or other object reachable from some name is git rev-list
; the option to use all names is --all
; so:
git rev-list --all
does the trick. This prints each hash ID to its standard output stream, so we will now collect all those IDs and turn them into their corresponding tree hashes.
One slight wrinkle here is in the phrasing above: this finds all commits or other objects, including annotated tag objects. An annotated tag is a name for another Git object, usually a commit object. So if we find that annotated tag v1.3
and commit 1234567...
both name your lost tree, we’ll see two hash IDs here. That’s probably actually what we want, but if not, you now know what to look for to change this.
In any case, to turn the rev-list
ID into a tree, we will want to use git rev-parse
. It’s possible that the ID cannot be turned into a tree: an annotated tag object, for instance, might tag a blob object rather than a commit. So for a fully robust solution we should check, using git rev-list --verify --quiet
and checking its return value:
lookfor=...put in the hash ID you are searching for ...
git rev-list --all |
while read hash; do
tree=$(git rev-parse --verify --quiet ${hash}^{tree}) || continue
if [ $tree = $lookfor ]; then
echo "found: $hash (type $(git cat-file -t $hash)) names tree $lookfor"
fi
done
(the above is untested but it’s too simple to be wrong).
If this finds any objects that refer to your lost tree, you now have the hashes (of commits and/or tags) for it.
If this finds no objects, that means that either you put in the wrong tree—see the caveat about untracked files—or the tree you have does not have a corresponding commit. That does not mean it never had one: perhaps your lost tree was part of an experimental branch that was deleted and had all its commits thrown out during garbage collection, for instance. It just means that no commit has that tree now, in your full repository (or its mirror-clone).