Reverting a file move

I was writing a bats test to verify that the status command can correctly list moved files and have run into a problem.

If I do the following:

$ pijul init mv-test && cd mv-test
$ echo 'fn main() { println!("Hello"); }' > foo.rs
$ pijul add foo.rs
$ pijul status -s
A foo.rs
$ pijul record -am "a" -A myself
Recorded patch AYiMBJEMd-ioFFwNRVioWR1KfxYhnMP3ii7cFORoRKPWfuxkPU22NGQPbWPAmT13zL1BAspdDXSUFGC4KWmwOhk
$ pijul mv foo.rs bar.rs
$ pijul status -s
R bar.rs # NOTE This is new
$ pijul revert -a
$ pijul status -s
D bar.rs
? foo.rs

It’s the last output that confuses me. The revert doesn’t seem to revert the move at all. Instead bar.rs is just deleted and foo.rs is unknown. So it appears that the revert was halfway successful - foo.rs was recovered and bar.rs has been deleted, but the state of each file seems incorrect.

I would expect the revert to restore the state of just when foo.rs had been recorded. What am I missing?

I tried doing pijul checkout master, but it seems to be stuck in that state. I get error: No such file or directory (os error 2), presumably because the bar.rs file is looked for, but not found.

@pmeunier @flobec Can you explain why the revert doesn’t seem to go through? Am I misunderstanding something or is it a problem in Pijul?

Hi! Sorry for not answering earlier. This will do the expected thing when you record (i.e. will notice that the correct name for that file is foo.rs), but I agree the UI is not great.

Since I didn’t write that part of status, I suspect this is based on a misunderstanding of the tree and inodes tables. I’ll try to fix that.

Edit: this is a actually a quite nasty bug in revert.

I was working on having status print file moves when I ran into this problem. I’d love to understand the difference, and how to figure out the old file name.

The best way to understand how this works is pijul info --debug, followed by dot -Tpdf -o debug_master.pdf debug_master, if you have graphviz installed.

Look at the arrows in blue and black, they are for files. Files have two vertices: a name and an untagged vertex. Files are never deleted; instead, when a file is moved, a new path is created from the untagged vertex of the directory it is in to a new name vertex, and from that new name vertex to its untagged vertex.

This happens in the nodes database. Then there is a link to actual files, with two tables, and their reverse:

  • inodes maps the id of the untagged vertex to a local identifier of files on the actual file system. Ids in the nodes database have no correlation with identifiers of actual files (these last identifiers are called “inodes”).

  • tree: this is just a tree of inodes, with no relation with nodes. It just lists the files currently tracked by pijul, and helps move/delete/add them.

(these two tables have a reverse table each, which can be used to find the path of an inode efficiently, or to record new files).

This means that a file move is encoded in patches as a sequence of a name deletion and a name addition, with the same down context. See record_moved_file in libpijul/src/record.rs, and how the structures built there (patch::Record) are converted to a sequence of Changes.

You can rely on the order in which things are stored in the patch, it has no reason to ever change.

1 Like

Thanks for the explanation!

So, if I have the following changes:

  1. Add foo.txt
  2. Rename foo.txt -> bar.txt
Hash: AUSlqR3eKdpPRl9VMjnSvyCbJ6nUpBsyChfdyStJR8GVccOvnrqM3gS1OX8S4xQh4eTe1OVs5GybNoCuqAdw6DU
Internal id: RKWpHd4p2k8
Authors: ["tj"]
Timestamp: 2017-08-10 14:29:13.637115407 UTC

    Rename foo.txt -> bar.txt

Hash: AaZxf5WigEbZb2O2-Zmfmn6nFpv2kMLc_z7d3-z1k9_EOeobFBeGdpu3ZTdi8BOAZUVUFFkOnTH3T5sZXX5DqK8
Internal id: pnF_laKARtk
Authors: ["tj"]
Timestamp: 2017-08-10 14:28:51.264401801 UTC

    Add foo.txt

and do pijul info --debug I obtain the following picture:

  • From your explanation, the presence of the four vertices with black/blue arrows represent two files?
  • What do the dotted black/blue lines represent? The deleted file?

Almost:

  • there is a root (the one with all 0s).

  • then, the two vertices starting with a671 represent the name and the file that were introduced by patch a671… More specifically, a6717f95a28046d90100000… is the name, and a6717f95a28046d90200000… is the file.

  • now, a dashed blue or dashed red arrow from some vertex a to some vertex b means that b is deleted (or at least that the patch that introduced that arrow thinks b is deleted). Here, we haven’t really deleted the file, but we have deleted its former name, and created a new name (the one starting with 44a5).

The contents of these lines used to be in these diagrams, but was removed at some point to look at bigger graphs (because of the limitatiosn of graphviz and PDF viewers).