I'm using libgit2 via git-rs. I'm trying to commit things which is working, however, old files are showing up as deleted and/or staged even after the commit. How can I clean this up?
let repo: Repository = ...
let head_commit = match repo.head() {
Ok(head) => head.peel_to_commit().ok(),
Err(_) => None,
};
let head_tree = match head_commit {
Some(ref commit) => commit.tree().ok(),
None => None,
};
let mut diff_options = DiffOptions::new();
diff_options
.include_untracked(true)
.recurse_untracked_dirs(true);
let diff_result = self
.repo
.diff_tree_to_workdir_with_index(head_tree.as_ref(), Some(&mut diff_options));
let diff_deltas: Vec<_> = match diff_result {
Ok(ref diff) => diff.deltas().collect(),
Err(_) => Vec::new(),
};
if diff_deltas.is_empty() {
info!("no files changed");
return Ok(());
}
let mut index = .repo.index()?;
for diff_delta in diff_deltas {
let delta = diff_delta.status();
match delta {
Delta::Added
| Delta::Copied
| Delta::Modified
| Delta::Renamed
| Delta::Untracked
| Delta::Unmodified => {
let path = diff_delta.new_file().path().unwrap();
debug!("Staging {:?} file: {:?}", delta, path);
index.add_path(path)?;
}
Delta::Deleted => {
let path = diff_delta.old_file().path().unwrap();
debug!("Unstaging {:?} file: {:?}", delta, path);
index.remove_path(path)?;
}
_ => debug!("skipping {:?} file", delta),
}
}
let index_oid = index.write_tree()?;
let index_tree = self.repo.find_tree(index_oid)?;
let sig = Signature::new(&self.committer.name, &self.committer.email, &time)?;
let parents: Vec<_> = [&head_commit].iter().flat_map(|c| c.as_ref()).collect();
repo.commit(Some("HEAD"), &sig, &sig, message, &index_tree, &parents)?;
index.clear().unwrap();
As #user2722968 pointed out, you must call both index.write() and index.write_tree().
index.write_tree() will create a tree (or trees) from the index, returning the root tree object. This can be used to create a commit.
The trouble here is that you have updated HEAD with the commit that you've created. When you run git status, then that will compare the working directory to the index to the HEAD commit.
In this case, you have made changes to the index, you've updated the index in memory and used that to update HEAD. But you haven't actually written the index itself to disk, so you will see that the working directory does not match the index for any modified files, nor does the index match HEAD. (The working directory and HEAD contents will match, though that is never actually tested by git.)
Once you call index.write() then the working directory, the index and the HEAD commit will all be identical, and thus you'll see the expected (empty) status.
Related
The following code is very simple. Open a file as a write, create a BufWriter using the file, and write a line of string.
The program reports no errors and returns an Ok(10) value, but the file just has no content and is empty.
#[tokio::test]
async fn save_file_async() {
let path = "./hello.txt";
let inner = tokio::fs::OpenOptions::new()
.create(true)
.write(true)
//.truncate(true)
.open(path)
.await
.unwrap();
let mut writer = tokio::io::BufWriter::new(inner);
println!(
"{} bytes wrote",
writer.write("1234567890".as_bytes()).await.unwrap()
);
}
Need an explicit flush:
writer.flush().await.unwrap();
I can't figure out how to mark a resolution after resolving merge conflicts with libgit2. calling git status on the repo returns Unmerged paths: and says both added: <'myfilename'>
Edit: Her is example code of how I got it to work for future reference.
git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_merge(repo, annotated_commit, 1, &merge_opts, &checkout_opts);
git_index* index = nullptr;
git_repository_index(&index, repo);
if (git_index_has_conflicts(index))
{
git_index_conflict_iterator* conflicts = nullptr;
git_index_entry entries[3];
git_index_conflict_iterator_new(&conflicts, index);
while (git_index_conflict_next(&entries[0], &entries[1], &entries[2], conflicts)
!= GIT_ITEROVER)
{
//Write desired content to file in working copy
handle_conflict(entries[2]->path);
git_index_conflict_remove(index, entries[2]->path);
git_index_add_bypath(index, entries[2]->path);
}
git_index_conflict_iterator_free(conflicts);
git_index_conflict_cleanup(index);
//At this point index does not have more conflicts so write it
git_index_write(index);
}
git_commit* parents[2];
git_reference* head_ref = nullptr;
git_repository_head(&head_ref, repo);
git_reference_peel((git_object**)&parents[0], head_ref, GIT_OBJECT_COMMIT);
git_commit_lookup(&parents[1], repo, git_annotated_commit_id(annotated));
git_tree* tree = nullptr;
git_oid tree_oid, commit_oid;
git_index_write_tree(&tree_oid, index);
git_tree_lookup(&tree, repo, &tree_oid);
git_commit_create(&commit_oid, repo,
git_reference_name(head_ref), signature, signature, NULL, "Merge",
tree, 2, (const git_commit**)parents);
git_tree_free(tree);
I have the following function which gives me an array called URLs
const storageRef = this.$fire.storage.ref().child(fileName)
try {
const snapshot = storageRef.put(element).then((snapshot) => {
snapshot.ref.getDownloadURL().then((url) => {
urls.push(url)
})
})
console.log('File uploaded.')
} catch (e) {
console.log(e.message)
}
});
console.log(urls)
console.log("about to run enter time with imageurls length " + urls.length)
When I run console.log(URLs) initially I do see the array like the following
[]
0: "testvalue"
length: 1
__proto__: Array(0)
However, there is a small information icon stating
This value was evaluated upon first expanding. The value may have changed since.
Because of this, when I try to get the length of URLs, I get zero, meaning the value is being updated.
Does anyone know what's happening? I am using Vue.JS/Nuxt.
To start off, here's my main bot.js code (Sorry if it seems like code spaghetti. I'm not the best with javascript):
const fs = require('fs');
const Discord = require('discord.js');
const client = new Discord.Client();
const auth = require('./auth.json');
const pack = require('./package.json');
const SQLite = require('better-sqlite3');
const sql = new SQLite('./scores.sqlite');
client.commands = new Discord.Collection();
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
client.commands.set(command.name, command);
}
client.on('ready', () => {
console.log(` Bot: ${client.user.tag}
Version: ${pack.version}
Logged in and clear for takeoff.`);
});
client.on("ready", () => {
const battle_leaderboard = sql.prepare("SELECT count(*) FROM sqlite_master WHERE type='table' AND name = 'scores';").get();
if (!battle_leaderboard['count(*)']) {
sql.prepare("CREATE TABLE scores (id TEXT PRIMARY KEY, user TEXT, guild TEXT, points INTEGER;").run();
sql.prepare("CREATE UNIQUE INDEX idx_scores_id ON scores (id);").run();
sql.pragma("synchronous = 1");
sql.pragma("journal_mode = wal");
}
client.getScore = sql.prepare("SELECT * FROM scores WHERE user = ? AND guild = ?");
client.setScore = sql.prepare("INSERT OR REPLACE INTO scores (id, user, guild, points) VALUES (#id, #user, #guild, #points);");
client.removeScore = sql.prepare("DELETE FROM scores WHERE user=?");
});
client.on('message', message => {
if (!message.content.startsWith(auth.prefix) || message.author.bot) return;
const args = message.content.slice(auth.prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
switch (command) {
case 'ping':
client.commands.get('ping').execute(message, args);
break;
case 'mimic':
client.commands.get('mimic').execute(message, args);
break;
case 'add_role':
client.commands.get('add_role').execute(message, args);
break;
case 'remove_role':
client.commands.get('remove_role').execute(message, args);
break;
case 'bl_add_point':
if (message.member.roles.some(r=>["Fight Officiator"])) {
let score;
let member = message.mentions.members.first() || client.members.get(args[0]);
score = client.getScore.get(member.id, member.guild.id);
if (!score) {
score = { id: `${member.guild.id}-${member.id}`, user: member.username, guild: member.guild.id, points: 0}
}
score.points ++;
client.setScore.run(score);
message.channel.send(`Looks like ${member} got a win! Good one.`);
} else {
message.channel.send(`Sorry, this is only usable by GFOs.`);
};
break;
case '!bl_delete':
if (message.member.roles.some(r=>["Fight Officiator"])) {
let member = message.mentions.users.first();
client.removeScore.run(`${member.guild.id}-${member.id}`)
message.channel.send(`${member} has been set to zero on the leaderboard!`);
} else {
message.channel.send(`Sorry, this is only usable by GFOs.`);
};
break;
case 'bl':
const top5 = sql.prepare("SELECT * FROM scores WHERE guild = ? ORDER BY points DESC LIMIT 5;").all(message.guild.id);
const battleleaderboard = new Discord.RichEmbed()
.setColor('#FFD700')
.setTitle('The Battle Leaderboard - Current Rankings')
.setAuthor(`RankBot`)
.setDescription('The top fighters are displayed here.')
.setTimestamp()
.setFooter(`DM a Gang Fight Officiator to set up a spar with another member and possibly get your name registered onto RankBot's leaderboard!`);
for (const data of top5) {
battleleaderboard.addField(client.users.get(data.user), `${data.points} Win(s)`);
}
message.channel.send(battleleaderboard);
break;
};
});
client.login(auth.token);
I'm creating a bot that should be pretty simple:
The bot is a RankBot (at least that's what i dubbed it as), which is supposed to track the amount of wins in battles (since this is for a discord about a fighting game) using a special role that manually increments those wins with a command, then being able to pull up a leaderboard embed showing the people with the most wins. I'm testing it in a discord separate from the one it's for with a friend, and it seemingly worked until we commanded the bot to open the leaderboard. Nothing was shown. We thought it was strange, since it worked before when only one user was in the database, and I went to the PowerShell Command Line to see if there was an error message, and this showed up.
C:\Users\USER\Documents\OF_RankBot\bot.js:89
battleleaderboard.addField(client.users.get(data.user).tag || client.members.get(data.user).tag, `${data.points} Win(s)`);
^
TypeError: Cannot read property 'tag' of undefined
We were confused by what happened, so I removed the tag part of the command and used it again. The embed showed up this time, but it looked like this.
I assumed that what went wrong was that since they both had the same number of wins listed, the part of the bl command that sorts the users was confused. I then went and used bl_add_point on me and tried again. The near exact same embed was shown.
My guess as to what went wrong was that I messed up at some point when setting up the bl_add_point command, but I'm not sure how to fix the issue.
This isn't part of the main problem, but I can't seem to get bl_delete working either. No errors are shown when I use it. In fact, seemingly nothing happens. I'm not sure if these issues are connected, but I really only need the main issue fixed.
Thanks in advance.
edit: I found out partially what happened with the bl_delete issue, which was I simply put the prefix into the case when the prefix was already defined. Now when I put in the command, the PowerShell responds with:
C:\Users\USER\Documents\OF_RankBot\bot.js:72
client.removeScore.run(`${member.guild.id}-${member.id}`)
^
TypeError: Cannot read property 'id' of undefined
In OS X Finder there is 'Comment' file property. It can be checked in finder by adding 'Comment' column or edited/checked after right clicking on file or folder and selecting 'Get info'.
How to read this value in swift or objective-c?
I checked already NSURL and none of them seems to be the right ones
Do not use the low-level extended attributes API to read Spotlight metadata. There's a proper Spotlight API for that. (It's called the File Metadata API.) Not only is it a pain in the neck, there's no guarantee that Apple will keep using the same extended attribute to store this information.
Use MDItemCreateWithURL() to create an MDItem for the file. Use MDItemCopyAttribute() with kMDItemFinderComment to obtain the Finder comment for the item.
Putting the pieces together (Ken Thomases reading answer above and writing answer link) you can extend URL with a computed property with a getter and a setter to read/write comments to your files:
update: Xcode 8.2.1 • Swift 3.0.2
extension URL {
var finderComment: String? {
get {
guard isFileURL else { return nil }
return MDItemCopyAttribute(MDItemCreateWithURL(kCFAllocatorDefault, self as CFURL), kMDItemFinderComment) as? String
}
set {
guard isFileURL, let newValue = newValue else { return }
let script = "tell application \"Finder\"\n" +
String(format: "set filePath to \"%#\" as posix file \n", absoluteString) +
String(format: "set comment of (filePath as alias) to \"%#\" \n", newValue) +
"end tell"
guard let appleScript = NSAppleScript(source: script) else { return }
var error: NSDictionary?
appleScript.executeAndReturnError(&error)
if let error = error {
print(error[NSAppleScript.errorAppName] as! String)
print(error[NSAppleScript.errorBriefMessage] as! String)
print(error[NSAppleScript.errorMessage] as! String)
print(error[NSAppleScript.errorNumber] as! NSNumber)
print(error[NSAppleScript.errorRange] as! NSRange)
}
}
}
}
As explained in the various answers to Mac OS X : add a custom meta data field to any file,
Finder comments can be read and set programmatically with getxattr() and setxattr(). They are stored as extended attribute
"com.apple.metadata:kMDItemFinderComment", and the value is a property
list.
This works even for files not indexed by Spotlight, such as those on a network server volume.
From the Objective-C code here
and here I made this simple Swift function
to read the Finder comment (now updated for Swift 4 and later):
func finderComment(url : URL) -> String? {
let XAFinderComment = "com.apple.metadata:kMDItemFinderComment"
let data = url.withUnsafeFileSystemRepresentation { fileSystemPath -> Data? in
// Determine attribute size:
let length = getxattr(fileSystemPath, XAFinderComment, nil, 0, 0, 0)
guard length >= 0 else { return nil }
// Create buffer with required size:
var data = Data(count: length)
// Retrieve attribute:
let result = data.withUnsafeMutableBytes { [count = data.count] in
getxattr(fileSystemPath, XAFinderComment, $0.baseAddress, count, 0, 0)
}
guard result >= 0 else { return nil }
return data
}
// Deserialize to String:
guard let data = data, let comment = try? PropertyListSerialization.propertyList(from: data,
options: [], format: nil) as? String else {
return nil
}
return comment
}
Example usage:
let url = URL(fileURLWithPath: "/path/to/file")
if let comment = finderComment(url: url) {
print(comment)
}
The function returns an optional string which is nil if the file
has no Finder comment, or if anything went wrong while retrieving it.