A walkthrough of the basic features of git-annex.

creating a repository

This is very straightforward. Just tell it a description of the repository.

# mkdir ~/annex
# cd ~/annex
# git init
# git annex init "my laptop"

adding a remote

Like any other git repository, git-annex repositories have remotes. Let's start by adding a USB drive as a remote.

# sudo mount /media/usb
# cd /media/usb
# git clone ~/annex
# cd annex
# git annex init "portable USB drive"
# git remote add laptop ~/annex
# cd ~/annex
# git remote add usbdrive /media/usb/annex

This is all standard ad-hoc distributed git repository setup. The only git-annex specific part is telling it the name of the new repository created on the USB drive.

Notice that both repos are set up as remotes of one another. This lets either get annexed files from the other. You'll want to do that even if you are using git in a more centralized fashion.

adding files

# cd ~/annex
# cp /tmp/big_file .
# cp /tmp/debian.iso .
# git annex add .
add big_file (checksum...) ok
add debian.iso (checksum...) ok
# git commit -a -m added

When you add a file to the annex and commit it, only a symlink to the annexed content is committed. The content itself is stored in git-annex's backend.

renaming files

# cd ~/annex
# git mv big_file my_cool_big_file
# mkdir iso
# git mv debian.iso iso/
# git commit -m moved

You can use any normal git operations to move files around, or even make copies or delete them.

Notice that, since annexed files are represented by symlinks, the symlink will break when the file is moved into a subdirectory. But, git-annex will fix this up for you when you commit -- it has a pre-commit hook that watches for and corrects broken symlinks.

getting file content

A repository does not always have all annexed file contents available. When you need the content of a file, you can use "git annex get" to make it available.

We can use this to copy everything in the laptop's annex to the USB drive.

# cd /media/usb/annex
# git fetch laptop; git merge laptop/master
# git annex get .
get my_cool_big_file (from laptop...) ok
get iso/debian.iso (from laptop...) ok

syncing

Notice that in the previous example, you had to git fetch and merge from laptop first. This lets git-annex know what has changed in laptop, and so it knows about the files present there and can get them.

If you have a lot of repositories to keep in sync, manually fetching and merging from them can become tedious. To automate it there is a handy sync command, which also even commits your changes for you.

# cd /media/usb/annex
# git annex sync
commit
nothing to commit (working directory clean)
ok
pull laptop
ok
push laptop
ok

After you run sync, the repository will be updated with all changes made to its remotes, and any changes in the repository will be pushed out to its remotes, where a sync will get them. This is especially useful when using git in a distributed fashion, without a central bare repository. See sync for details.

transferring files: When things go wrong

After a while, you'll have several annexes, with different file contents. You don't have to try to keep all that straight; git-annex does location tracking for you. If you ask it to get a file and the drive or file server is not accessible, it will let you know what it needs to get it:

# git annex get video/hackity_hack_and_kaxxt.mov
get video/_why_hackity_hack_and_kaxxt.mov (not available)
  Unable to access these remotes: usbdrive, server
  Try making some of these repositories available:
    5863d8c0-d9a9-11df-adb2-af51e6559a49  -- my home file server
    58d84e8a-d9ae-11df-a1aa-ab9aa8c00826  -- portable USB drive
    ca20064c-dbb5-11df-b2fe-002170d25c55  -- backup SATA drive
failed
# sudo mount /media/usb
# git annex get video/hackity_hack_and_kaxxt.mov
get video/hackity_hack_and_kaxxt.mov (from usbdrive...) ok

removing files

You can always drop files safely. Git-annex checks that some other annex has the file before removing it.

# git annex drop iso/debian.iso
drop iso/Debian_5.0.iso ok

removing files: When things go wrong

Before dropping a file, git-annex wants to be able to look at other remotes, and verify that they still have a file. After all, it could have been dropped from them too. If the remotes are not mounted/available, you'll see something like this.

# git annex drop important_file other.iso
drop important_file (unsafe)
  Could only verify the existence of 0 out of 1 necessary copies
  Unable to access these remotes: usbdrive
  Try making some of these repositories available:
    58d84e8a-d9ae-11df-a1aa-ab9aa8c00826  -- portable USB drive
    ca20064c-dbb5-11df-b2fe-002170d25c55  -- backup SATA drive
  (Use --force to override this check, or adjust annex.numcopies.)
failed
drop other.iso (unsafe)
  Could only verify the existence of 0 out of 1 necessary copies
      No other repository is known to contain the file.
  (Use --force to override this check, or adjust annex.numcopies.)
failed

Here you might --force it to drop important_file if you trust your backup. But other.iso looks to have never been copied to anywhere else, so if it's something you want to hold onto, you'd need to transfer it to some other repository before dropping it.

modifying annexed files

Normally, the content of files in the annex is prevented from being modified. That's a good thing, because it might be the only copy, you wouldn't want to lose it in a fumblefingered mistake.

# echo oops > my_cool_big_file
bash: my_cool_big_file: Permission denied

In order to modify a file, it should first be unlocked.

# git annex unlock my_cool_big_file
unlock my_cool_big_file (copying...) ok

That replaces the symlink that normally points at its content with a copy of the content. You can then modify the file like any regular file. Because it is a regular file.

(If you decide you don't need to modify the file after all, or want to discard modifications, just use git annex lock.)

When you git commit, git-annex's pre-commit hook will automatically notice that you are committing an unlocked file, and add its new content to the annex. The file will be replaced with a symlink to the new content, and this symlink is what gets committed to git in the end.

# echo "now smaller, but even cooler" > my_cool_big_file
# git commit my_cool_big_file -m "changed an annexed file"
add my_cool_big_file ok
[master 64cda67] changed an annexed file
 1 files changed, 1 insertions(+), 1 deletions(-)

There is one problem with using git commit like this: Git wants to first stage the entire contents of the file in its index. That can be slow for big files (sorta why git-annex exists in the first place). So, the automatic handling on commit is a nice safety feature, since it prevents the file content being accidentally committed into git. But when working with big files, it's faster to explicitly add them to the annex yourself before committing.

# echo "now smaller, but even cooler yet" > my_cool_big_file
# git annex add my_cool_big_file
add my_cool_big_file ok
# git commit my_cool_big_file -m "changed an annexed file"

using ssh remotes

So far in this walkthrough, git-annex has been used with a remote repository on a USB drive. But it can also be used with a git remote that is truely remote, a host accessed by ssh.

Say you have a desktop on the same network as your laptop and want to clone the laptop's annex to it:

# git clone ssh://mylaptop/home/me/annex ~/annex
# cd ~/annex
# git annex init "my desktop"

Now you can get files and they will be transferred (using rsync via ssh):

# git annex get my_cool_big_file
get my_cool_big_file (getting UUID for origin...) (from origin...)
SHA256-s86050597--6ae2688bc533437766a48aa19f2c06be14d1bab9c70b468af445d4f07b65f41e  100% 2159     2.1KB/s   00:00
ok

When you drop files, git-annex will ssh over to the remote and make sure the file's content is still there before removing it locally:

# git annex drop my_cool_big_file
drop my_cool_big_file (checking origin..) ok

Note that normally git-annex prefers to use non-ssh remotes, like a USB drive, before ssh remotes. They are assumed to be faster/cheaper to access, if available. There is a annex-cost setting you can configure in .git/config to adjust which repositories it prefers. See the man page for details.

Also, note that you need full shell access for this to work -- git-annex needs to be able to ssh in and run commands. Or at least, your shell needs to be able to run the git-annex-shell command.

moving file content between repositories

Often you will want to move some file contents from a repository to some other one. For example, your laptop's disk is getting full; time to move some files to an external disk before moving another file from a file server to your laptop. Doing that by hand (by using git annex get and git annex drop) is possible, but a bit of a pain. git annex move makes it very easy.

# git annex move my_cool_big_file --to usbdrive
move my_cool_big_file (to usbdrive...) ok
# git annex move video/hackity_hack_and_kaxxt.mov --from fileserver
move video/hackity_hack_and_kaxxt.mov (from fileserver...)
SHA256-s86050597--6ae2688bc533437766a48aa19f2c06be14d1bab9c70b468af445d4f07b65f41e   100%   82MB 199.1KB/s   07:02
ok

unused data

It's possible for data to accumulate in the annex that no files in any branch point to anymore. One way it can happen is if you git rm a file without first calling git annex drop. And, when you modify an annexed file, the old content of the file remains in the annex. Another way is when migrating between key-value backends.

This might be historical data you want to preserve, so git-annex defaults to preserving it. So from time to time, you may want to check for such data and eliminate it ty ent. var dateElements; hook("onload", getDates); function getDates() { dateElements = getElementsByClass('relativedate'); for (var i = 0; i < dateElements.length; i++) { var elt = dateElements[i]; var title = elt.attributes.title; var d = new Date(title ? title.value : elt.innerHTML); if (! isNaN(d)) { dateElements[i].date=d; elt.title=elt.innerHTML; } } showDates(); } function showDates() { for (var i = 0; i < dateElements.length; i++) { var elt = dateElements[i]; var d = elt.date; if (! isNaN(d)) { elt.innerHTML=relativeDate(d); } } setTimeout(showDates,30000); // keep updating every 30s } var timeUnits = [ { unit: 'year', seconds: 60 * 60 * 24 * 364 }, { unit: 'month', seconds: 60 * 60 * 24 * 30 }, { unit: 'day', seconds: 60 * 60 * 24 }, { unit: 'hour', seconds: 60 * 60 }, { unit: 'minute', seconds: 60 }, ]; function relativeDate(date) { var now = new Date(); var offset = date.getTime() - now.getTime(); var seconds = Math.round(Math.abs(offset) / 1000); // hack to avoid reading just in the future if there is a minor // amount of clock slip if (offset >= 0 && seconds < 30 * 60 * 60) { return "just now"; } var ret = ""; var shown = 0; for (i = 0; i < timeUnits.length; i++) { if (seconds >= timeUnits[i].seconds) { var num = Math.floor(seconds / timeUnits[i].seconds); seconds -= num * timeUnits[i].seconds; if (ret) ret += "and "; ret += num + " " + timeUnits[i].unit + (num > 1 ? "s" : "") + " "; if (++shown == 2) break; } else if (shown) break; } if (! ret) ret = "less than a minute " return ret + (offset < 0 ? "ago" : "from now"); } ./usr/share/doc/git-annex/html/walkthrough.html0000644000000000000000000010766312027312164020467 0ustar rootroot walkthrough

A walkthrough of the basic features of git-annex.

creating a repository

This is very straightforward. Just tell it a description of the repository.

# mkdir ~/annex
# cd ~/annex
# git init
# git annex init "my laptop"

adding a remote

Like any other git repository, git-annex repositories have remotes. Let's start by adding a USB drive as a remote.

# sudo mount /media/usb
# cd /media/usb
# git clone ~/annex
# cd annex
# git annex init "portable USB drive"
# git remote add laptop ~/annex
# cd ~/annex
# git remote add usbdrive /media/usb/annex

This is all standard ad-hoc distributed git repository setup. The only git-annex specific part is telling it the name of the new repository created on the USB drive.

Notice that both repos are set up as remotes of one another. This lets either get annexed files from the other. You'll want to do that even if you are using git in a more centralized fashion.

adding files

# cd ~/annex
# cp /tmp/big_file .
# cp /tmp/debian.iso .
# git annex add .
add big_file (checksum...) ok
add debian.iso (checksum...) ok
# git commit -a -m added

When you add a file to the annex and commit it, only a symlink to the annexed content is committed. The content itself is stored in git-annex's backend.

renaming files

# cd ~/annex
# git mv big_file my_cool_big_file
# mkdir iso
# git mv debian.iso iso/
# git commit -m moved

You can use any normal git operations to move files around, or even make copies or delete them.

Notice that, since annexed files are represented by symlinks, the symlink will break when the file is moved into a subdirectory. But, git-annex will fix this up for you when you commit -- it has a pre-commit hook that watches for and corrects broken symlinks.

getting file content

A repository does not always have all annexed file contents available. When you need the content of a file, you can use "git annex get" to make it available.

We can use this to copy everything in the laptop's annex to the USB drive.

# cd /media/usb/annex
# git fetch laptop; git merge laptop/master
# git annex get .
get my_cool_big_file (from laptop...) ok
get iso/debian.iso (from laptop...) ok

syncing

Notice that in the previous example, you had to git fetch and merge from laptop first. This lets git-annex know what has changed in laptop, and so it knows about the files present there and can get them.

If you have a lot of repositories to keep in sync, manually fetching and merging from them can become tedious. To automate it there is a handy sync command, which also even commits your changes for you.

# cd /media/usb/annex
# git annex sync
commit
nothing to commit (working directory clean)
ok
pull laptop
ok
push laptop
ok

After you run sync, the repository will be updated with all changes made to its remotes, and any changes in the repository will be pushed out to its remotes, where a sync will get them. This is especially useful when using git in a distributed fashion, without a central bare repository. See sync for details.

transferring files: When things go wrong

After a while, you'll have several annexes, with different file contents. You don't have to try to keep all that straight; git-annex does location tracking for you. If you ask it to get a file and the drive or file server is not accessible, it will let you know what it needs to get it:

# git annex get video/hackity_hack_and_kaxxt.mov
get video/_why_hackity_hack_and_kaxxt.mov (not available)
  Unable to access these remotes: usbdrive, server
  Try making some of these repositories available:
    5863d8c0-d9a9-11df-adb2-af51e6559a49  -- my home file server
    58d84e8a-d9ae-11df-a1aa-ab9aa8c00826  -- portable USB drive
    ca20064c-dbb5-11df-b2fe-002170d25c55  -- backup SATA drive
failed
# sudo mount /media/usb
# git annex get video/hackity_hack_and_kaxxt.mov
get video/hackity_hack_and_kaxxt.mov (from usbdrive...) ok

removing files

You can always drop files safely. Git-annex checks that some other annex has the file before removing it.

# git annex drop iso/debian.iso
drop iso/Debian_5.0.iso ok

removing files: When things go wrong

Before dropping a file, git-annex wants to be able to look at other remotes, and verify that they still have a file. After all, it could have been dropped from them too. If the remotes are not mounted/available, you'll see something like this.

# git annex drop important_file other.iso
drop important_file (unsafe)
  Could only verify the existence of 0 out of 1 necessary copies
  Unable to access these remotes: usbdrive
  Try making some of these repositories available:
    58d84e8a-d9ae-11df-a1aa-ab9aa8c00826  -- portable USB drive
    ca20064c-dbb5-11df-b2fe-002170d25c55  -- backup SATA drive
  (Use --force to override this check, or adjust annex.numcopies.)
failed
drop other.iso (unsafe)
  Could only verify the existence of 0 out of 1 necessary copies
      No other repository is known to contain the file.
  (Use --force to override this check, or adjust annex.numcopies.)
failed

Here you might --force it to drop important_file if you trust your backup. But other.iso looks to have never been copied to anywhere else, so if it's something you want to hold onto, you'd need to transfer it to some other repository before dropping it.

modifying annexed files

Normally, the content of files in the annex is prevented from being modified. That's a good thing, because it might be the only copy, you wouldn't want to lose it in a fumblefingered mistake.

# echo oops > my_cool_big_file
bash: my_cool_big_file: Permission denied

In order to modify a file, it should first be unlocked.

# git annex unlock my_cool_big_file
unlock my_cool_big_file (copying...) ok

That replaces the symlink that normally points at its content with a copy of the content. You can then modify the file like any regular file. Because it is a regular file.

(If you decide you don't need to modify the file after all, or want to discard modifications, just use git annex lock.)

When you git commit, git-annex's pre-commit hook will automatically notice that you are committing an unlocked file, and add its new content to the annex. The file will be replaced with a symlink to the new content, and this symlink is what gets committed to git in the end.

# echo "now smaller, but even cooler" > my_cool_big_file
# git commit my_cool_big_file -m "changed an annexed file"
add my_cool_big_file ok
[master 64cda67] changed an annexed file
 1 files changed, 1 insertions(+), 1 deletions(-)

There is one problem with using git commit like this: Git wants to first stage the entire contents of the file in its index. That can be slow for big files (sorta why git-annex exists in the first place). So, the automatic handling on commit is a nice safety feature, since it prevents the file content being accidentally committed into git. But when working with big files, it's faster to explicitly add them to the annex yourself before committing.

# echo "now smaller, but even cooler yet" > my_cool_big_file
# git annex add my_cool_big_file
add my_cool_big_file ok
# git commit my_cool_big_file -m "changed an annexed file"

using ssh remotes

So far in this walkthrough, git-annex has been used with a remote repository on a USB drive. But it can also be used with a git remote that is truely remote, a host accessed by ssh.

Say you have a desktop on the same network as your laptop and want to clone the laptop's annex to it:

# git clone ssh://mylaptop/home/me/annex ~/annex
# cd ~/annex
# git annex init "my desktop"

Now you can get files and they will be transferred (using rsync via ssh):

# git annex get my_cool_big_file
get my_cool_big_file (getting UUID for origin...) (from origin...)
SHA256-s86050597--6ae2688bc533437766a48aa19f2c06be14d1bab9c70b468af445d4f07b65f41e  100% 2159     2.1KB/s   00:00
ok

When you drop files, git-annex will ssh over to the remote and make sure the file's content is still there before removing it locally:

# git annex drop my_cool_big_file
drop my_cool_big_file (checking origin..) ok

Note that normally git-annex prefers to use non-ssh remotes, like a USB drive, before ssh remotes. They are assumed to be faster/cheaper to access, if available. There is a annex-cost setting you can configure in .git/config to adjust which repositories it prefers. See the man page for details.

Also, note that you need full shell access for this to work -- git-annex needs to be able to ssh in and run commands. Or at least, your shell needs to be able to run the git-annex-shell command.

moving file content between repositories

Often you will want to move some file contents from a repository to some other one. For example, your laptop's disk is getting full; time to move some files to an external disk before moving another file from a file server to your laptop. Doing that by hand (by using git annex get and git annex drop) is possible, but a bit of a pain. git annex move makes it very easy.

# git annex move my_cool_big_file --to usbdrive
move my_cool_big_file (to usbdrive...) ok
# git annex move video/hackity_hack_and_kaxxt.mov --from fileserver
move video/hackity_hack_and_kaxxt.mov (from fileserver...)
SHA256-s86050597--6ae2688bc533437766a48aa19f2c06be14d1bab9c70b468af445d4f07b65f41e   100%   82MB 199.1KB/s   07:02
ok

unused data

It's possible for data to accumulate in the annex that no files in any branch point to anymore. One way it can happen is if you git rm a file without first calling git annex drop. And, when you modify an annexed file, the old content of the file remains in the annex. Another way is when migrating between key-value backends.

This might be historical data you want to preserve, so git-annex defaults to preserving it. So from time to time, you may want to check for such data and eliminate it ty ent. var dateElements; hook("onload", getDates); function getDates() { dateElements = getElementsByClass('relativedate'); for (var i = 0; i < dateElements.length; i++) { var elt = dateElements[i]; var title = elt.attributes.title; var d = new Date(title ? title.value : elt.innerHTML); if (! isNaN(d)) { dateElements[i].date=d; elt.title=elt.innerHTML; } } showDates(); } function showDates() { for (var i = 0; i < dateElements.length; i++) { var elt = dateElements[i]; var d = elt.date; if (! isNaN(d)) { elt.innerHTML=relativeDate(d); } } setTimeout(showDates,30000); // keep updating every 30s } var timeUnits = [ { unit: 'year', seconds: 60 * 60 * 24 * 364 }, { unit: 'month', seconds: 60 * 60 * 24 * 30 }, { unit: 'day', seconds: 60 * 60 * 24 }, { unit: 'hour', seconds: 60 * 60 }, { unit: 'minute', seconds: 60 }, ]; function relativeDate(date) { var now = new Date(); var offset = date.getTime() - now.getTime(); var seconds = Math.round(Math.abs(offset) / 1000); // hack to avoid reading just in the future if there is a minor // amount of clock slip if (offset >= 0 && seconds < 30 * 60 * 60) { return "just now"; } var ret = ""; var shown = 0; for (i = 0; i < timeUnits.length; i++) { if (seconds >= timeUnits[i].seconds) { var num = Math.floor(seconds / timeUnits[i].seconds); seconds -= num * timeUnits[i].seconds; if (ret) ret += "and "; ret += num + " " + timeUnits[i].unit + (num > 1 ? "s" : "") + " "; if (++shown == 2) break; } else if (shown) break; } if (! ret) ret = "less than a minute " return ret + (offset < 0 ? "ago" : "from now"); } ./usr/share/doc/git-annex/html/walkthrough.html0000644000000000000000000010766312027312164020467 0ustar rootroot walkthrough

A walkthrough of the basic features of git-annex.

creating a repository

This is very straightforward. Just tell it a description of the repository.

# mkdir ~/annex
# cd ~/annex
# git init
# git annex init "my laptop"

adding a remote

Like any other git repository, git-annex repositories have remotes. Let's start by adding a USB drive as a remote.

# sudo mount /media/usb
# cd /media/usb
# git clone ~/annex
# cd annex
# git annex init "portable USB drive"
# git remote add laptop ~/annex
# cd ~/annex
# git remote add usbdrive /media/usb/annex

This is all standard ad-hoc distributed git repository setup. The only git-annex specific part is telling it the name of the new repository created on the USB drive.

Notice that both repos are set up as remotes of one another. This lets either get annexed files from the other. You'll want to do that even if you are using git in a more centralized fashion.

adding files

# cd ~/annex
# cp /tmp/big_file .
# cp /tmp/debian.iso .
# git annex add .
add big_file (checksum...) ok
add debian.iso (checksum...) ok
# git commit -a -m added

When you add a file to the annex and commit it, only a symlink to the annexed content is committed. The content itself is stored in git-annex's backend.

renaming files

# cd ~/annex
# git mv big_file my_cool_big_file
# mkdir iso
# git mv debian.iso iso/
# git commit -m moved

You can use any normal git operations to move files around, or even make copies or delete them.

Notice that, since annexed files are represented by symlinks, the symlink will break when the file is mov