-
1. Úvod
- 1.1 Správa verzí
- 1.2 Stručná historie systému Git
- 1.3 Základy systému Git
- 1.4 Příkazový řádek
- 1.5 Instalace systému Git
- 1.6 První nastavení systému Git
- 1.7 Získání nápovědy
- 1.8 Shrnutí
-
2. Základy práce se systémem Git
-
3. Větve v systému Git
- 3.1 Větve v kostce
- 3.2 Základy větvení a slučování
- 3.3 Správa větví
- 3.4 Postupy při práci s větvemi
- 3.5 Vzdálené větve
- 3.6 Přeskládání
- 3.7 Shrnutí
-
4. Git na serveru
- 4.1 Protokoly
- 4.2 Zprovoznění Gitu na serveru
- 4.3 Generování veřejného klíče SSH
- 4.4 Nastavení serveru
- 4.5 Démon Git
- 4.6 Chytrý HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Možnosti hostování u třetí strany
- 4.10 Shrnutí
-
5. Distribuovaný Git
- 5.1 Distribuované pracovní postupy
- 5.2 Přispívání do projektu
- 5.3 Správa projektu
- 5.4 Shrnutí
-
6. GitHub
-
7. Git Tools
- 7.1 Revision Selection
- 7.2 Interactive Staging
- 7.3 Stashing and Cleaning
- 7.4 Signing Your Work
- 7.5 Searching
- 7.6 Rewriting History
- 7.7 Reset Demystified
- 7.8 Advanced Merging
- 7.9 Rerere
- 7.10 Ladění v systému Git
- 7.11 Submodules
- 7.12 Bundling
- 7.13 Replace
- 7.14 Credential Storage
- 7.15 Shrnutí
-
8. Customizing Git
- 8.1 Git Configuration
- 8.2 Atributy Git
- 8.3 Git Hooks
- 8.4 An Example Git-Enforced Policy
- 8.5 Shrnutí
-
9. Git a ostatní systémy
- 9.1 Git as a Client
- 9.2 Migrating to Git
- 9.3 Shrnutí
-
10. Git Internals
- 10.1 Plumbing and Porcelain
- 10.2 Git Objects
- 10.3 Git References
- 10.4 Balíčkové soubory
- 10.5 The Refspec
- 10.6 Přenosové protokoly
- 10.7 Správa a obnova dat
- 10.8 Environment Variables
- 10.9 Shrnutí
-
A1. Appendix A: Git in Other Environments
- A1.1 Graphical Interfaces
- A1.2 Git in Visual Studio
- A1.3 Git in Eclipse
- A1.4 Git in Bash
- A1.5 Git in Zsh
- A1.6 Git in Powershell
- A1.7 Shrnutí
-
A2. Appendix B: Embedding Git in your Applications
- A2.1 Command-line Git
- A2.2 Libgit2
- A2.3 JGit
-
A3. Appendix C: Git Commands
- A3.1 Setup and Config
- A3.2 Getting and Creating Projects
- A3.3 Basic Snapshotting
- A3.4 Branching and Merging
- A3.5 Sharing and Updating Projects
- A3.6 Inspection and Comparison
- A3.7 Debugging
- A3.8 Patching
- A3.9 Email
- A3.10 External Systems
- A3.11 Administration
- A3.12 Plumbing Commands
10.7 Git Internals - Správa a obnova dat
Správa a obnova dat
Occasionally, you may have to do some cleanup – make a repository more compact, clean up an imported repository, or recover lost work. Tato část se na některé z těchto scénářů zaměří.
Maintenance
Occasionally, Git automatically runs a command called “auto gc”.
Ve většině případů neprovede tento příkaz vůbec nic.
Pokud však identifikuje příliš mnoho volných objektů (objektů nezabalených do balíčkového souboru) nebo balíčkových souborů, spustí Git plnou verzi příkazu git gc
.
The “gc” stands for garbage collect, and the command does a number of things: it gathers up all the loose objects and places them in packfiles, it consolidates packfiles into one big packfile, and it removes objects that aren’t reachable from any commit and are a few months old.
Příkaz auto gc můžete spustit také ručně:
$ git gc --auto
I tentokrát platí, že příkaz většinou neprovede nic.
Aby Git spustil skutečný příkaz gc, musíte mít kolem 7000 volných objektů nebo více než 50 balíčkových souborů.
Tyto hodnoty můžete změnit podle svých potřeb v konfiguračním nastavení gc.auto
a gc.autopacklimit
.
Další operací, kterou gc
provede, je zabalení referencí do jediného souboru.
Řekněme, že váš repozitář obsahuje tyto větve a značky:
$ find .git/refs -type f
.git/refs/heads/experiment
.git/refs/heads/master
.git/refs/tags/v1.0
.git/refs/tags/v1.1
If you run git gc
, you’ll no longer have these files in the refs
directory.
Git je pro zvýšení účinnosti přesune do souboru .git/packed-refs
, jenž má následující podobu:
$ cat .git/packed-refs
# pack-refs with: peeled fully-peeled
cac0cab538b970a37ea1e769cbbde608743bc96d refs/heads/experiment
ab1afef80fac8e34258ff41fc1b867c702daa24b refs/heads/master
cac0cab538b970a37ea1e769cbbde608743bc96d refs/tags/v1.0
9585191f37f7b0fb9444f35a9bf50de191beadc2 refs/tags/v1.1
^1a410efbd13591db07496601ebc7a059dd55cfe9
If you update a reference, Git doesn’t edit this file but instead writes a new file to refs/heads
.
To get the appropriate SHA-1 for a given reference, Git checks for that reference in the refs
directory and then checks the packed-refs
file as a fallback.
However, if you can’t find a reference in the refs
directory, it’s probably in your packed-refs
file.
Všimněte si také posledního řádku souboru, který začíná znakem ^
.
Tento řádek znamená, že značka bezprostředně nad ním je anotovaná a tento řádek je revize, na niž tato anotovaná značka ukazuje.
Data Recovery
Někdy se může stát, že nedopatřením přijdete o revizi Git. Většinou k tomu dochází tak, že násilím smažete větev, která uchovávala část vaší práce, a vy po čase zjistíte, že byste tuto větev přece jen potřebovali. Stejně tak jste mohli provést tvrdý reset větve a tím zavrhnout revize, z nichž nyní něco potřebujete. Pokud se už něco takového stane, jak dostanete své revize zpět?
Here’s an example that hard-resets the master branch in your test repository to an older commit and then recovers the lost commits. First, let’s review where your repository is at this point:
$ git log --pretty=oneline
ab1afef80fac8e34258ff41fc1b867c702daa24b modified repo a bit
484a59275031909e19aadb7c92262719cfcdf19a added repo.rb
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
Nyní vrátíme větev master
zpět na prostřední revizi:
$ git reset --hard 1a410efbd13591db07496601ebc7a059dd55cfe9
HEAD is now at 1a410ef third commit
$ git log --pretty=oneline
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
You’ve effectively lost the top two commits – you have no branch from which those commits are reachable. You need to find the latest commit SHA-1 and then add a branch that points to it. The trick is finding that latest commit SHA-1 – it’s not like you’ve memorized it, right?
Nejrychlejší cestou často bývá použít nástroj git reflog
.
As you’re working, Git silently records what your HEAD is every time you change it.
Vždy když zapíšete revizi nebo změníte větve, je reflog aktualizován.
The reflog is also updated by the git update-ref
command, which is another reason to use it instead of just writing the SHA-1 value to your ref files, as we covered in Git References.
You can see where you’ve been at any time by running git reflog
:
$ git reflog
1a410ef HEAD@{0}: reset: moving to 1a410ef
ab1afef HEAD@{1}: commit: modified repo.rb a bit
484a592 HEAD@{2}: commit: added repo.rb
Vidíme tu obě revize, jichž jsme se zbavili, ale není tu k nim mnoho informací.
Chcete-li zobrazit stejné informace v užitečnějším formátu, můžete spustit příkaz git log -g
, jímž získáte normální výstup příkazu log pro reflog.
$ git log -g
commit 1a410efbd13591db07496601ebc7a059dd55cfe9
Reflog: HEAD@{0} (Scott Chacon <schacon@gmail.com>)
Reflog message: updating HEAD
Author: Scott Chacon <schacon@gmail.com>
Date: Fri May 22 18:22:37 2009 -0700
third commit
commit ab1afef80fac8e34258ff41fc1b867c702daa24b
Reflog: HEAD@{1} (Scott Chacon <schacon@gmail.com>)
Reflog message: updating HEAD
Author: Scott Chacon <schacon@gmail.com>
Date: Fri May 22 18:15:24 2009 -0700
modified repo.rb a bit
Zdá se, že revize úplně dole je hledanou ztracenou revizí. Můžete ji obnovit tak, že na ní vytvoříte novou větev.
Na revizi můžete vytvořit například větev recover-branch
(ab1afef):
$ git branch recover-branch ab1afef
$ git log --pretty=oneline recover-branch
ab1afef80fac8e34258ff41fc1b867c702daa24b modified repo a bit
484a59275031909e19aadb7c92262719cfcdf19a added repo.rb
1a410efbd13591db07496601ebc7a059dd55cfe9 third commit
cac0cab538b970a37ea1e769cbbde608743bc96d second commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
Cool – now you have a branch named recover-branch
that is where your master
branch used to be, making the first two commits reachable again.
Next, suppose your loss was for some reason not in the reflog – you can simulate that by removing recover-branch
and deleting the reflog.
Now the first two commits aren’t reachable by anything:
$ git branch -D recover-branch
$ rm -Rf .git/logs/
Protože se data pro reflog uchovávají v adresáři .git/logs/
, nemáte evidentně žádný reflog.
Jak lze tedy v tuto chvíli ztracenou revizi obnovit?
Jednou z možností je použít nástroj git fsck
, který zkontroluje integritu vaší databáze.
If you run it with the --full
option, it shows you all objects that aren’t pointed to by another object:
$ git fsck --full
Checking object directories: 100% (256/256), done.
Checking objects: 100% (18/18), done.
dangling blob d670460b4b4aece5915caf5c68d12f560a9fe3e4
dangling commit ab1afef80fac8e34258ff41fc1b867c702daa24b
dangling tree aea790b9a58f6cf6f2804eeac9f0abbe9631e4c9
dangling blob 7108f7ecb345ee9d0084193f147cdad4d2998293
In this case, you can see your missing commit after the string “dangling commit”. You can recover it the same way, by adding a branch that points to that SHA-1.
Removing Objects
Systém Git nabízí velké množství úžasných funkcí a možností. Je však jedna věc, která vám může způsobovat problém. Je jí fakt, že příkaz git clone
stáhne vždy celou historii projektu, všechny verze všech souborů.
Je však jedna věc, která vám může způsobovat problém.
Je jí fakt, že příkaz git clone
stáhne vždy celou historii projektu, všechny verze všech souborů.
Because it’s reachable from the history, it will always be there.
This can be a huge problem when you’re converting Subversion or Perforce repositories into Git. Because you don’t download the whole history in those systems, this type of addition carries few consequences. Pokud provedete import do systému Git z jiného systému, nebo jiným způsobem zjistíte, že je váš repozitář výrazně větší, než by měl být, můžete vyhledat a odstranit velké objekty následovně.
Be warned: this technique is destructive to your commit history. It rewrites every commit object since the earliest tree you have to modify to remove a large file reference. If you do this immediately after an import, before anyone has started to base work on the commit, you’re fine – otherwise, you have to notify all contributors that they must rebase their work onto your new commits.
To demonstrate, you’ll add a large file into your test repository, remove it in the next commit, find it, and remove it permanently from the repository. Nejprve do historie přidejte velký objekt:
$ curl https://www.kernel.org/pub/software/scm/git/git-2.1.0.tar.gz > git.tgz
$ git add git.tgz
$ git commit -m 'add git tarball'
[master 7b30847] add git tarball
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 git.tgz
Oops – you didn’t want to add a huge tarball to your project. Raději se ho zbavme:
$ git rm git.tgz
rm 'git.tgz'
$ git commit -m 'oops - removed large tarball'
[master dadf725] oops - removed large tarball
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 git.tgz
Now, gc
your database and see how much space you’re using:
$ git gc
Counting objects: 17, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (13/13), done.
Writing objects: 100% (17/17), done.
Total 17 (delta 1), reused 10 (delta 0)
You can run the count-objects
command to quickly see how much space you’re using:
$ git count-objects -v
count: 7
size: 32
in-pack: 17
packs: 1
size-pack: 4868
prune-packable: 0
garbage: 0
size-garbage: 0
The size-pack
entry is the size of your packfiles in kilobytes, so you’re using almost 5MB.
Before the last commit, you were using closer to 2K – clearly, removing the file from the previous commit didn’t remove it from your history.
Every time anyone clones this repository, they will have to clone all 5MB just to get this tiny project, because you accidentally added a big file.
Let’s get rid of it.
Nejprve ho budete muset najít.
V tomto případě víte, o jaký soubor se jedná.
But suppose you didn’t; how would you identify what file or files were taking up so much space?
Spustíte-li příkaz git gc
, všechny objekty jsou v balíčkovém souboru. Velké objekty lze identifikovat spuštěním jiného nízkoúrovňového příkazu, git verify-pack
, a seřazením podle třetího pole ve výpisu, v němž je uvedena velikost souboru.
You can also pipe it through the tail
command because you’re only interested in the last few largest files:
$ git verify-pack -v .git/objects/pack/pack-29…69.idx \
| sort -k 3 -n \
| tail -3
dadf7258d699da2c8d89b09ef6670edb7d5f91b4 commit 229 159 12
033b4468fa6b2a9547a70d88d1bbe8bf3f9ed0d5 blob 22044 5792 4977696
82c99a3e86bb1267b236a4b6eff7868d97489af1 blob 4975916 4976258 1438
The big object is at the bottom: 5MB.
To find out what file it is, you’ll use the rev-list
command, which you used briefly in Enforcing a Specific Commit-Message Format.
If you pass --objects
to rev-list
, it lists all the commit SHA-1s and also the blob SHA-1s with the file paths associated with them.
You can use this to find your blob’s name:
$ git rev-list --objects --all | grep 82c99a3
82c99a3e86bb1267b236a4b6eff7868d97489af1 git.tgz
Nyní potřebujete odstranit tento soubor ze všech minulých stromů. Pomocí snadného příkazu lze zjistit, jaké revize tento soubor změnil:
$ git log --oneline --branches -- git.tgz
dadf725 oops - removed large tarball
7b30847 add git tarball
You must rewrite all the commits downstream from 7b30847
to fully remove this file from your Git history.
To do so, you use filter-branch
, which you used in Rewriting History:
$ git filter-branch --index-filter \
'git rm --ignore-unmatch --cached git.tgz' -- 7b30847^..
Rewrite 7b30847d080183a1ab7d18fb202473b3096e9f34 (1/2)rm 'git.tgz'
Rewrite dadf7258d699da2c8d89b09ef6670edb7d5f91b4 (2/2)
Ref 'refs/heads/master' was rewritten
The --index-filter
option is similar to the --tree-filter
option used in Rewriting History, except that instead of passing a command that modifies files checked out on disk, you’re modifying your staging area or index each time.
Rather than remove a specific file with something like rm file
, you have to remove it with git rm --cached
– you must remove it from the index, not from disk.
The reason to do it this way is speed – because Git doesn’t have to check out each revision to disk before running your filter, the process can be much, much faster.
Pokud chcete, můžete provést stejný úkon i pomocí parametru --tree-filter
.
The --ignore-unmatch
option to git rm
tells it not to error out if the pattern you’re trying to remove isn’t there.
Finally, you ask filter-branch
to rewrite your history only from the 7b30847
commit up, because you know that is where this problem started.
Bez této konkretizace začne proces od začátku a bude trvat zbytečně dlouho.
Vaše historie už neobsahuje referenci na problémový soubor.
Obsahuje ho však stále ještě reflog a v adresáři .git/refs/original
také nová sada referencí, které Git přidal při spuštění příkazu filter-branch
. Budete je proto muset odstranit a databázi znovu zabalit.
Před novým zabalením je třeba odstranit vše, co na tyto staré revize ukazuje:
$ rm -Rf .git/refs/original
$ rm -Rf .git/logs/
$ git gc
Counting objects: 15, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (11/11), done.
Writing objects: 100% (15/15), done.
Total 15 (delta 1), reused 12 (delta 0)
Let’s see how much space you saved.
$ git count-objects -v
count: 11
size: 4904
in-pack: 15
packs: 1
size-pack: 8
prune-packable: 0
garbage: 0
size-garbage: 0
The packed repository size is down to 8K, which is much better than 5MB.
You can see from the size value that the big object is still in your loose objects, so it’s not gone; but it won’t be transferred on a push or subsequent clone, which is what is important.
If you really wanted to, you could remove the object completely by running git prune
with the --expire
option:
$ git prune --expire now
$ git count-objects -v
count: 0
size: 0
in-pack: 15
packs: 1
size-pack: 8
prune-packable: 0
garbage: 0
size-garbage: 0