Keep a History of All Modifications to a Text File – Version Control

fileslinuxversion control

I have a plain text file (not containing source code). I often modify it (adding lines, editing existing lines, or any other possible modification). For any modification, I would like to automatically record:

  • what has been modified (the diff information);
  • the date and time of the modification.

(Ideally, I would also like to be able to obtain the version of my file at a specific time, but this is a plus, not essential).

This is surely possible with Git, but it's too powerful and complex. I don't want to deal with add, commit messages, push, etc. each time. I would simply like to edit the file with vi (or equivalent), save it, and automatically record the modification as above (its diff and its time).

Is there a tool to accomplish this in Linux?


Update: Thanks for all the suggestions and the several solutions that have been introduced. I have nothing against git, but I explicitly wished to avoid it (for several reason, last but not least the fact that I don't know it enough). The tool which is closest to the above requirements (no git, no commit messages, little or nothing overhead) is RCS. It is file-based and it is exactly what I was looking for. This even avoids the use of a script, provides the previous versions of the file and avoids the customization for vi.


The requirements of the question were precise; many opinions have been given, but the question is not – per se – that much opinion-based. Then, obviously, the same goal can be achieved through a tool or through a script, but this apply in many other cases as well.

Best Answer

You could try the venerable RCS (package "rcs") as @steeldriver mentioned, a non-modern version control system that works on a per-file basis with virtually no overhead or complication. There are multiple ways to use it, but one possibility:

  • Create an RCS subdirectory, where the version history will be stored.
  • Edit your file
  • Check in your changes: ci -l -m -t- myfile
  • Repeat

If you store this text in your file:

$RCSfile$
$Revision$
$Date$

then RCS will populate those strings with information about your revision and its datestamp, once you check it in (technically, when you check it out).

The file stored in RCS/ will be called myfile,v and will contain the diffs between each revision. Of course there's more to learn about RCS. You can look at the manpages for ci, co, rcs, rcsdiff and others.

Here's some more information:

  • If you skip creating the RCS/ directory, then the archive will appear in the same directory as your file.
  • You "check in" a file with ci to record a version of it in the archive (the *,v file in the RCS/ directory). Check-in has the weird side effect of removing your file, leaving your data only present in the *,v archive. To avoid this side effect, use -l or -u with the ci command.
  • You "check out" a file with co to reconstitute it from the archive.
  • You "lock" a file to make it writable and prevent others from writing to it, which would create a "merge" situation. In your case, with only one user modifying the file, "locked" means writable and "unlocked" means read-only. If you modify and "unlocked" file (by forcing a write to it), ci will complain when you try to check the changes in (so, avoid doing that).
  • Since you're the only one editing your file, you have a choice of scenarios: you can keep your file read-only (unlocked) or writable (locked). I use unlocked mode for files that I don't expect to change often, as that prevents me from accidentally modifying them, because they're read-only, even for me. I use locked mode for files that I'm actively modifying, but when I want to keep a revision history of the contents.
  • Using -l with ci or co will lock it, leaving it writable. Without -l it will be read-only with co or it will be removed altogether with ci. Use ci -u to leave the file in read-only mode after checking its contents into the archive.
  • Using -m. will prevent ci from asking for a revision message.
  • Using -t- will prevent ci from asking for an initial message (when the archive file is first created).
  • Using -M with ci or co will keep the timestamp of a file in sync with the timestamp of the file at the time of check-in.
  • co -r1.2 -p -q myfile will print revision 1.2 of myfile to stdout. Without the -p option, and assuming that myfile is "unlocked" (read-only), then co -r1.2 myfile will overwrite myfile with a read-only copy of revision 1.2 of myfile. -q disables the informational messages.
  • You can create "branches", with revisions like 1.3.1.1. I don't recommend this as it gets confusing fast. I prefer to keep with a linear flow of revisions.

So, if you prefer to keep your file always writable, you could use ci -l -M -m -t- myfile. You can use rcsdiff myfile to see the differences between the current contents of myfile and the most recent checked-in version. You can use rcsdiff -r1.2 -r1.4 myfile to see the differences between revisions 1.2 and 1.4 of myfile.

The archive file is just a text file, whose format is documented in man rcsfile. However, don't attempt to edit the archive file directly. IMO, the text-based archive file, the absolute minimal extra baggage (only a single archive file), and keyword substitution are RCS's biggest strengths and what makes it a great tool for local-only, single-user, single-file-at-a-time versioning. If I were redesigning RCS, I would remove the complications beyond this scenario (e.g. multi-user, branching), which I think are better handled by more modern distributed version control systems.

As with any command, there are some quirks; you should play around with test files until you understand the workflow you want for yourself. Then, for best results, embed your favorite options into a script so you don't have to remember the likes of -t-, for example.

Related Question