pijul_org / pijul

Fixing the "unrecord also unrecords dependents"

By blabla on March 25, 2019
This patch is not signed.
887oRoafFHkaKczU7zcLVd5UdFALHi54wR2GPxZ8harr6aTrKjz2k47MiYXSXkTQZJnV2WvKdJJishmX6dY5sw2o
This patch is in the following branches:
latest
master
testing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74


75
76
77
78
79
                                })
use clap::{Arg, ArgMatches, SubCommand};

use super::{ask, default_explain, validate_base58, BasicOptions, StaticSubcommand};
use std::collections::HashSet;
use std::path::Path;

use error::Error;
use libpijul::fs_representation::{patch_file_name, RepoRoot};
use libpijul::patch::Patch;
use libpijul::{unrecord_no_resize, Hash, HashRef, PatchId};
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::mem::drop;

pub fn invocation() -> StaticSubcommand {
    return SubCommand::with_name("unrecord")
        .about("Unrecord some patches (remove them without reverting them)")
        .arg(
            Arg::with_name("repository")
                .long("repository")
                .help("Local repository.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("branch")
                .long("branch")
                .help("Branch.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("patch")
                .help("Patch to unrecord.")
                .takes_value(true)
                .multiple(true)
                .validator(validate_base58),
        );
}

pub fn run(args: &ArgMatches) -> Result<(), Error> {
    let opts = BasicOptions::from_args(args)?;
    let patches: Option<HashSet<Hash>> = args
        .values_of("patch")
        .map(|ps| ps.map(|x| Hash::from_base58(x).unwrap()).collect());
    let mut increase = 409600;
    let repo = opts.open_and_grow_repo(increase)?;
    let branch_name = opts.branch();

    let mut patches: HashMap<_, _> = if let Some(ref patches) = patches {
        let txn = repo.txn_begin()?;
        if let Some(branch) = txn.get_branch(&branch_name) {
            let mut patches_ = HashMap::new();
            for h in patches.iter() {
                debug!("unrecording {:?}", h);

                if let Some(internal) = txn.get_internal(h.as_ref()) {
                    if txn.get_patch(&branch.patches, internal).is_some() {
                        let patch = load_patch(&opts.repo_root, h.as_ref());
                        patches_.insert(h.to_owned(), patch);

                        for (_, revdep) in txn
                            .iter_revdep(Some((internal, None)))
                            .take_while(|&(q, _)| q == internal)
                        {
                            // If the branch has patch revdep, and
                            // revdep is not also to be unrecorded.
                            if patches
                                .iter()
                                .any(|p| txn.get_internal(p.as_ref()).unwrap() == revdep)
                            {
                                continue;
                            }
                            if txn.get_patch(&branch.patches, revdep).is_some() {
                                let ext = txn.get_external(revdep).unwrap();
                                let patch = load_patch(&opts.repo_root, ext);
                                patches_.insert(ext.to_owned(), patch);
                                return Err(Error::PatchIsDependedUpon {
                                    hash: h.to_owned(),
                                    dependent: ext.to_owned(),
                                });
                            }
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
        patch_hash: libpijul::Hash,
    },
    PatchIsDependedUpon {
        hash: libpijul::Hash,
        dependent: libpijul::Hash,
    },
    SshKeyNotFound {
        path: PathBuf,
    },
    NoHomeDir,
    ExtraDepNotOnBranch {
        hash: libpijul::Hash,
    },
    PendingChanges,
    EmptyPatchName,
    InvalidPatchName,
    CannotSpawnEditor {
        editor: String,
        cause: String,
    },
    InvalidDate {
        date: String,
    },
    PartialPullOverHttp,
    UnknownHost {
        host: String,
    },
    NoAuthor,
    NonFastForwardPush,
    NotSigningAuthor,
    EmptyPassword,
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            Error::IO(ref e) => e.fmt(f),
            Error::Term(ref e) => e.fmt(f),
            Error::Repository(ref e) => e.fmt(f),
            Error::UTF8(ref e) => e.fmt(f),
            Error::Hex(ref e) => e.fmt(f),
            Error::SSH(ref e) => e.fmt(f),
            Error::SSHKeys(ref e) => e.fmt(f),
            Error::Reqwest(ref e) => e.fmt(f),
            Error::TomlDe(ref e) => e.fmt(f),
            Error::TomlSer(ref e) => e.fmt(f),
            Error::StripPrefix(ref e) => e.fmt(f),
            Error::Regex(ref e) => e.fmt(f),
            Error::ThrusshConfig(ref e) => e.fmt(f),
            Error::Failure(ref e) => e.fmt(f),
            Error::HookFailed { ref cmd } => write!(f, "Hook failed: {}", cmd),
            Error::InARepository { ref path } => write!(f, "In a repository: {:?}", path),
            Error::NotInARepository => write!(f, "Not in a repository"),
            Error::MissingRemoteRepository => write!(f, "Missing remote repository"),
            Error::InvalidPath { ref path } => write!(f, "Invalid path: {:?}", path),
            Error::FileNotInRepository { ref path } => write!(f, "File not in repository: {:?}", path),
            Error::WrongHash => write!(f, "Wrong hash"),
            Error::BranchAlreadyExists => write!(f, "Branch already exists"),
            Error::CannotDeleteCurrentBranch => write!(f, "Cannot delete current branch"),
            Error::NoSuchBranch => write!(f, "No such branch"),
            Error::IsDirectory => write!(f, "Is a directory"),
            Error::CannotParseRemote => write!(f, "Cannot parse remote address"),
            Error::WillNotOverwriteKeyFile { ref path } => write!(f, "Will not overwrite key file {:?}", path),
            Error::BranchDoesNotHavePatch { ref branch_name, ref patch } => write!(f, "Branch {:?} does not have patch {}", branch_name, patch.to_base58()),
            Error::PatchNotFound { ref repo_root, ref patch_hash } => write!(f, "Patch {} not found in repository {:?}", patch_hash.to_base58(), repo_root),
            Error::PatchIsDependedUpon { ref dependent, ref hash } => write!(f, "Patch {} depends on {:?}", dependent.to_base58(), hash.to_base58()),
            Error::SshKeyNotFound { ref path } => write!(f, "SSH key not found in: {:?}", path),
            Error::NoHomeDir => write!(f, "No home dir"),
            Error::ExtraDepNotOnBranch { ref hash } => write!(f, "Extra dependencies can only be added if they are on the same branch as the current record: {:?}", hash),
            Error::PendingChanges => write!(f, "There are pending changes in the repository."),
            Error::EmptyPatchName => write!(f, "Empty patch name"),
            Error::InvalidPatchName => write!(f, "Invalid patch name"),
            Error::CannotSpawnEditor { ref editor, ref cause } => write!(f, "Cannot start editor {:?} ({:?})", editor, cause),
            Error::InvalidDate { ref date } => write!(f, "Invalid date: {:?}", date),
            Error::PartialPullOverHttp => write!(f, "Partial pull over HTTP is not (yet) supported"),
            Error::UnknownHost { ref host } => write!(f, "Unknown host: {}", host),
            Error::NoAuthor => write!(f, "No authors were given"),
            Error::NonFastForwardPush => write!(f, "This push is not fast-forward. If this is really what you mean, use --force."),
            Error::NotSigningAuthor => write!(f, "The user id for the signing key doesn't match the author. Authors must be of the form `rms@xyzcorp.com` or `Robert M. Smith <rms@xyzcorp.com>`"),
            Error::EmptyPassword => write!(f, "Empty password"),
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::IO(ref e) => e.description(),
            Error::Term(ref e) => e.description(),
            Error::Repository(ref e) => e.description(),
            Error::UTF8(ref e) => e.description(),
            Error::Hex(ref e) => e.description(),
            Error::SSH(ref e) => e.description(),
            Error::SSHKeys(ref e) => e.description(),
            Error::Reqwest(ref e) => e.description(),
            Error::TomlDe(ref e) => e.description(),
            Error::TomlSer(ref e) => e.description(),
            Error::StripPrefix(ref e) => e.description(),
            Error::Regex(ref e) => e.description(),
            Error::ThrusshConfig(ref e) => e.description(),
            Error::Failure(ref e) => e.name().unwrap_or("Unknown failure"),
            Error::HookFailed { .. } => "Hook failed",
            Error::InARepository { .. } => "In a repository",
            Error::NotInARepository => "Not in a repository",
            Error::MissingRemoteRepository => "Missing remote repository",
            Error::InvalidPath { .. } => "Invalid path",
            Error::FileNotInRepository { .. } => "File not in repository",
            Error::WrongHash => "Wrong hash",
            Error::BranchAlreadyExists => "Branch already exists",
            Error::CannotDeleteCurrentBranch => "Cannot delete current branch",
            Error::NoSuchBranch => "No such branch",
            Error::IsDirectory => "Is a directory",
            Error::CannotParseRemote => "Cannot parse remote address",
            Error::WillNotOverwriteKeyFile { .. } => "Will not overwrite key file",
            Error::BranchDoesNotHavePatch { .. } => "Branch does not have patch",
            Error::PatchNotFound { .. } => "Patch not found in repository",
            Error::PatchIsDependedUpon { .. } => "Patch depended upon",
            Error::SshKeyNotFound { .. } => "SSH key not found",