mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-29 20:43:37 +03:00
Use Duration rather than various integer times for time
This commit is contained in:
parent
14e05adae6
commit
d55260c96a
@ -31,8 +31,8 @@
|
||||
>
|
||||
{#if $baseServiceBusy$}
|
||||
<div class="sync-btn__busy-label">busy…</div>
|
||||
{:else if $baseBranch?.lastFetched}
|
||||
<TimeAgo date={$baseBranch?.lastFetched} />
|
||||
{:else if $baseBranch?.lastFetchedAt}
|
||||
<TimeAgo date={$baseBranch?.lastFetchedAt} />
|
||||
{/if}
|
||||
</Tag>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { RemoteHunk } from '$lib/vbranches/types';
|
||||
import { RemoteHunk, dateFromDuration } from '$lib/vbranches/types';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
|
||||
export type Operation =
|
||||
@ -61,6 +61,6 @@ export class Snapshot {
|
||||
linesRemoved!: number;
|
||||
filesChanged!: string[];
|
||||
details?: SnapshotDetails;
|
||||
@Transform((obj) => new Date(obj.value * 1000))
|
||||
@Transform(dateFromDuration)
|
||||
createdAt!: Date;
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ export const baseBranch = {
|
||||
behind: 0,
|
||||
upstreamCommits: [],
|
||||
recentCommits: [remoteCommit0],
|
||||
lastFetchedMs: 1714843209991
|
||||
lastFetchedAt: new Date(1714843209991)
|
||||
};
|
||||
|
||||
export const user: User = {
|
||||
|
@ -3,7 +3,28 @@ import { splitMessage } from '$lib/utils/commitMessage';
|
||||
import { hashCode } from '$lib/utils/string';
|
||||
import { isDefined, notNull } from '$lib/utils/typeguards';
|
||||
import { convertRemoteToWebUrl } from '$lib/utils/url';
|
||||
import { Type, Transform } from 'class-transformer';
|
||||
import { Type, Transform, type TransformFnParams } from 'class-transformer';
|
||||
|
||||
interface Duration {
|
||||
secs: number;
|
||||
nanos: number;
|
||||
}
|
||||
|
||||
function isDuration(object: any): object is Duration {
|
||||
const secsIsNumber = typeof object.secs === 'number';
|
||||
const nanosIsNumber = typeof object.nanos === 'number';
|
||||
|
||||
return secsIsNumber && nanosIsNumber;
|
||||
}
|
||||
|
||||
export function dateFromDuration(params: TransformFnParams): Date | undefined {
|
||||
if (!params.value) return;
|
||||
|
||||
if (!isDuration(params.value)) {
|
||||
throw Error('Expected a Duration object');
|
||||
}
|
||||
return new Date(params.value.secs * 1000 + params.value.nanos / 1000000);
|
||||
}
|
||||
|
||||
export type ChangeType =
|
||||
/// Entry does not exist in old version
|
||||
@ -16,9 +37,7 @@ export type ChangeType =
|
||||
export class Hunk {
|
||||
id!: string;
|
||||
diff!: string;
|
||||
@Transform((obj) => {
|
||||
return new Date(obj.value);
|
||||
})
|
||||
@Transform(dateFromDuration)
|
||||
modifiedAt!: Date;
|
||||
filePath!: string;
|
||||
hash?: string;
|
||||
@ -43,7 +62,7 @@ export class LocalFile {
|
||||
@Type(() => Hunk)
|
||||
hunks!: Hunk[];
|
||||
expanded?: boolean;
|
||||
@Transform((obj) => new Date(obj.value))
|
||||
@Transform(dateFromDuration)
|
||||
modifiedAt!: Date;
|
||||
// This indicates if a file has merge conflict markers generated and not yet resolved.
|
||||
// This is true for files after a branch which does not apply cleanly (Branch.isMergeable == false) is applied.
|
||||
@ -121,7 +140,7 @@ export class Branch {
|
||||
// If the branch has been already applied, then it was either performed cleanly or we generated conflict markers in the diffs.
|
||||
// (therefore this field is applicable for stashed/unapplied or remote branches, i.e. active == false)
|
||||
isMergeable!: Promise<boolean>;
|
||||
@Transform((obj) => new Date(obj.value))
|
||||
@Transform(dateFromDuration)
|
||||
updatedAt!: Date;
|
||||
// Indicates that branch is default target for new changes
|
||||
selectedForChanges!: boolean;
|
||||
@ -152,7 +171,7 @@ export class Commit {
|
||||
id!: string;
|
||||
author!: Author;
|
||||
description!: string;
|
||||
@Transform((obj) => new Date(obj.value))
|
||||
@Transform(dateFromDuration)
|
||||
createdAt!: Date;
|
||||
isRemote!: boolean;
|
||||
isIntegrated!: boolean;
|
||||
@ -197,7 +216,7 @@ export class RemoteCommit {
|
||||
id!: string;
|
||||
author!: Author;
|
||||
description!: string;
|
||||
@Transform((obj) => new Date(obj.value * 1000))
|
||||
@Transform(dateFromDuration)
|
||||
createdAt!: Date;
|
||||
changeId!: string;
|
||||
isSigned!: boolean;
|
||||
@ -361,16 +380,13 @@ export class BaseBranch {
|
||||
upstreamCommits!: RemoteCommit[];
|
||||
@Type(() => RemoteCommit)
|
||||
recentCommits!: RemoteCommit[];
|
||||
lastFetchedMs?: number;
|
||||
@Transform(dateFromDuration)
|
||||
lastFetchedAt?: Date;
|
||||
|
||||
actualPushRemoteName(): string {
|
||||
return this.pushRemoteName || this.remoteName;
|
||||
}
|
||||
|
||||
get lastFetched(): Date | undefined {
|
||||
return this.lastFetchedMs ? new Date(this.lastFetchedMs) : undefined;
|
||||
}
|
||||
|
||||
get pushRepoBaseUrl(): string {
|
||||
return convertRemoteToWebUrl(this.pushRemoteUrl);
|
||||
}
|
||||
|
@ -51,7 +51,8 @@ fn list_snapshots(repo_dir: &str) -> Result<()> {
|
||||
let project = project_from_path(repo_dir);
|
||||
let snapshots = project.list_snapshots(100, None)?;
|
||||
for snapshot in snapshots {
|
||||
let ts = chrono::DateTime::from_timestamp(snapshot.created_at / 1000, 0);
|
||||
// Using chrono for formatting the timestamp
|
||||
let ts = chrono::DateTime::from_timestamp(snapshot.created_at.as_secs() as i64, 0);
|
||||
let details = snapshot.details;
|
||||
if let (Some(ts), Some(details)) = (ts, details) {
|
||||
println!("{} {} {}", ts, snapshot.id, details.operation);
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use super::{Oid, Result, Signature, Tree};
|
||||
use bstr::BStr;
|
||||
|
||||
@ -54,8 +56,8 @@ impl<'repo> Commit<'repo> {
|
||||
self.commit.parent(n).map(Into::into).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn time(&self) -> git2::Time {
|
||||
self.commit.time()
|
||||
pub fn time(&self) -> Duration {
|
||||
Duration::from_secs(self.commit.time().seconds().try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn author(&self) -> Signature<'_> {
|
||||
|
@ -7,6 +7,7 @@ use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use strum::EnumString;
|
||||
|
||||
use serde::Serialize;
|
||||
@ -19,7 +20,7 @@ pub struct Snapshot {
|
||||
/// The sha of the commit that represents the snapshot
|
||||
pub id: String,
|
||||
/// Snapshot creation time in epoch milliseconds
|
||||
pub created_at: i64,
|
||||
pub created_at: Duration,
|
||||
/// The number of working directory lines added in the snapshot
|
||||
pub lines_added: usize,
|
||||
/// The number of working directory lines removed in the snapshot
|
||||
@ -304,7 +305,7 @@ mod tests {
|
||||
let commit_sha = "1234567890".to_string();
|
||||
let commit_message =
|
||||
"Create a new snapshot\n\nBody text 1\nBody text2\n\nBody text 3\n\nVersion: 1\nOperation: CreateCommit\nFoo: Bar\n".to_string();
|
||||
let created_at = 1234567890;
|
||||
let created_at = Duration::from_secs(1234567890);
|
||||
let details = SnapshotDetails::from_str(&commit_message.clone()).unwrap();
|
||||
let snapshot = Snapshot {
|
||||
id: commit_sha.clone(),
|
||||
|
@ -3,6 +3,7 @@ use git2::FileMode;
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use std::{fs, path::PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
@ -349,7 +350,7 @@ impl Oplog for Project {
|
||||
lines_added,
|
||||
lines_removed,
|
||||
files_changed,
|
||||
created_at: commit.time().seconds(),
|
||||
created_at: Duration::from_secs(commit.time().seconds().try_into().unwrap()),
|
||||
});
|
||||
|
||||
if snapshots.len() >= limit {
|
||||
@ -363,7 +364,7 @@ impl Oplog for Project {
|
||||
lines_added: 0,
|
||||
lines_removed: 0,
|
||||
files_changed: Vec::new(), // Fix: Change 0 to an empty vector
|
||||
created_at: commit.time().seconds(),
|
||||
created_at: Duration::from_secs(commit.time().seconds().try_into().unwrap()),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ use std::{
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use super::conflicts;
|
||||
use crate::error::{AnyhowContextExt, Code, ErrorWithContext};
|
||||
use crate::{
|
||||
askpass, error,
|
||||
git::{self, credentials::HelpError, Url},
|
||||
@ -15,6 +14,10 @@ use crate::{
|
||||
ssh, users,
|
||||
virtual_branches::{Branch, BranchId},
|
||||
};
|
||||
use crate::{
|
||||
error::{AnyhowContextExt, Code, ErrorWithContext},
|
||||
time,
|
||||
};
|
||||
|
||||
pub struct Repository {
|
||||
pub git_repository: git::Repository,
|
||||
@ -175,7 +178,7 @@ impl Repository {
|
||||
let branch = self.git_repository.find_branch(&target_branch_refname)?;
|
||||
let commit_id = branch.peel_to_commit()?.id();
|
||||
|
||||
let now = crate::time::now_ms();
|
||||
let now = time::now_ms();
|
||||
let branch_name = format!("test-push-{now}");
|
||||
|
||||
let refname =
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
path::{self, PathBuf},
|
||||
time,
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -38,16 +38,16 @@ pub struct ApiProject {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum FetchResult {
|
||||
Fetched {
|
||||
timestamp: time::SystemTime,
|
||||
timestamp: SystemTime,
|
||||
},
|
||||
Error {
|
||||
timestamp: time::SystemTime,
|
||||
timestamp: SystemTime,
|
||||
error: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl FetchResult {
|
||||
pub fn timestamp(&self) -> &time::SystemTime {
|
||||
pub fn timestamp(&self) -> &SystemTime {
|
||||
match self {
|
||||
FetchResult::Fetched { timestamp } | FetchResult::Error { timestamp, .. } => timestamp,
|
||||
}
|
||||
@ -57,7 +57,7 @@ impl FetchResult {
|
||||
#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
|
||||
pub struct CodePushState {
|
||||
pub id: git::Oid,
|
||||
pub timestamp: time::SystemTime,
|
||||
pub timestamp: SystemTime,
|
||||
}
|
||||
|
||||
pub type ProjectId = Id<Project>;
|
||||
|
@ -1,12 +1,43 @@
|
||||
use std::time::UNIX_EPOCH;
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
|
||||
/// Gets the duration of time since the Unix epoch.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if the system time is set before the Unix epoch.
|
||||
pub fn now() -> Duration {
|
||||
UNIX_EPOCH
|
||||
.elapsed()
|
||||
.expect("system time is set before the Unix epoch")
|
||||
}
|
||||
|
||||
/// Gets the number of milliseconds since the Unix epoch.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if the system time is set before the Unix epoch.
|
||||
pub fn now_ms() -> u128 {
|
||||
UNIX_EPOCH
|
||||
.elapsed()
|
||||
.expect("system time is set before the Unix epoch")
|
||||
.as_millis()
|
||||
now().as_millis()
|
||||
}
|
||||
|
||||
pub mod duration_serializer {
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&duration.as_millis().to_string())
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let millis_str = String::deserialize(deserializer)?;
|
||||
let millis = millis_str
|
||||
.parse::<u64>()
|
||||
.map_err(serde::de::Error::custom)?;
|
||||
Ok(Duration::from_millis(millis))
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
use std::{path::Path, time};
|
||||
use std::{
|
||||
path::Path,
|
||||
time::{self, Duration},
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use serde::Serialize;
|
||||
@ -31,7 +34,7 @@ pub struct BaseBranch {
|
||||
pub behind: usize,
|
||||
pub upstream_commits: Vec<RemoteCommit>,
|
||||
pub recent_commits: Vec<RemoteCommit>,
|
||||
pub last_fetched_ms: Option<u128>,
|
||||
pub last_fetched_at: Option<Duration>,
|
||||
}
|
||||
|
||||
pub fn get_base_branch_data(
|
||||
@ -210,7 +213,7 @@ pub fn set_base_branch(
|
||||
},
|
||||
);
|
||||
|
||||
let now_ms = crate::time::now_ms();
|
||||
let now = crate::time::now();
|
||||
|
||||
let (upstream, upstream_head) = if let git::Refname::Local(head_name) = &head_name {
|
||||
let upstream_name = target_branch_ref.with_branch(head_name.branch());
|
||||
@ -244,8 +247,8 @@ pub fn set_base_branch(
|
||||
applied: true,
|
||||
upstream,
|
||||
upstream_head,
|
||||
created_timestamp_ms: now_ms,
|
||||
updated_timestamp_ms: now_ms,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
head: current_head_commit.id(),
|
||||
tree: super::write_tree_onto_commit(
|
||||
project_repository,
|
||||
@ -646,13 +649,13 @@ pub fn target_to_base_branch(
|
||||
behind: upstream_commits.len(),
|
||||
upstream_commits,
|
||||
recent_commits,
|
||||
last_fetched_ms: project_repository
|
||||
last_fetched_at: project_repository
|
||||
.project()
|
||||
.project_data_last_fetch
|
||||
.as_ref()
|
||||
.map(FetchResult::timestamp)
|
||||
.copied()
|
||||
.map(|t| t.duration_since(time::UNIX_EPOCH).unwrap().as_millis()),
|
||||
.map(|t| t.duration_since(time::UNIX_EPOCH).unwrap()),
|
||||
};
|
||||
Ok(base)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use std::{fmt::Display, ops::RangeInclusive, str::FromStr};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use bstr::ByteSlice;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::git::diff;
|
||||
|
||||
@ -10,7 +11,7 @@ pub type HunkHash = md5::Digest;
|
||||
#[derive(Debug, Eq, Clone)]
|
||||
pub struct Hunk {
|
||||
pub hash: Option<HunkHash>,
|
||||
pub timestamp_ms: Option<u128>,
|
||||
pub timestamp: Option<Duration>,
|
||||
pub start: u32,
|
||||
pub end: u32,
|
||||
pub locked_to: Vec<diff::HunkLock>,
|
||||
@ -22,7 +23,7 @@ impl From<&diff::GitHunk> for Hunk {
|
||||
start: hunk.new_start,
|
||||
end: hunk.new_start + hunk.new_lines,
|
||||
hash: Some(Hunk::hash_diff(&hunk.diff_lines)),
|
||||
timestamp_ms: None,
|
||||
timestamp: None,
|
||||
locked_to: hunk.locked_to.to_vec(),
|
||||
}
|
||||
}
|
||||
@ -44,7 +45,7 @@ impl From<RangeInclusive<u32>> for Hunk {
|
||||
start: *range.start(),
|
||||
end: *range.end(),
|
||||
hash: None,
|
||||
timestamp_ms: None,
|
||||
timestamp: None,
|
||||
locked_to: vec![],
|
||||
}
|
||||
}
|
||||
@ -83,27 +84,28 @@ impl FromStr for Hunk {
|
||||
None
|
||||
};
|
||||
|
||||
let timestamp_ms = if let Some(raw_timestamp_ms) = range.next() {
|
||||
Some(
|
||||
raw_timestamp_ms
|
||||
.parse::<u128>()
|
||||
.context(format!("failed to parse timestamp_ms of range: {}", s))?,
|
||||
)
|
||||
let timestamp = if let Some(raw_timestamp_ms) = range.next() {
|
||||
let parsed_millis = raw_timestamp_ms
|
||||
.parse::<u64>()
|
||||
.context(format!("failed to parse timestamp_ms of range: {}", s))?;
|
||||
Some(Duration::from_millis(parsed_millis))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Hunk::new(start, end, hash, timestamp_ms)
|
||||
Hunk::new(start, end, hash, timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Hunk {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}-{}", self.start, self.end)?;
|
||||
match (&self.hash, &self.timestamp_ms) {
|
||||
(Some(hash), Some(timestamp_ms)) => write!(f, "-{:x}-{}", hash, timestamp_ms),
|
||||
match (&self.hash, &self.timestamp) {
|
||||
(Some(hash), Some(timestamp)) => {
|
||||
write!(f, "-{:x}-{}", hash, timestamp.as_millis())
|
||||
}
|
||||
(Some(hash), None) => write!(f, "-{:x}", hash),
|
||||
(None, Some(timestamp_ms)) => write!(f, "--{}", timestamp_ms),
|
||||
(None, Some(timestamp)) => write!(f, "--{}", timestamp.as_millis()),
|
||||
(None, None) => Ok(()),
|
||||
}
|
||||
}
|
||||
@ -114,14 +116,14 @@ impl Hunk {
|
||||
start: u32,
|
||||
end: u32,
|
||||
hash: Option<HunkHash>,
|
||||
timestamp_ms: Option<u128>,
|
||||
timestamp: Option<Duration>,
|
||||
) -> Result<Self> {
|
||||
if start > end {
|
||||
Err(anyhow!("invalid range: {}-{}", start, end))
|
||||
} else {
|
||||
Ok(Hunk {
|
||||
hash,
|
||||
timestamp_ms,
|
||||
timestamp,
|
||||
start,
|
||||
end,
|
||||
locked_to: vec![],
|
||||
@ -134,13 +136,13 @@ impl Hunk {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_timestamp(mut self, timestamp_ms: u128) -> Self {
|
||||
self.timestamp_ms = Some(timestamp_ms);
|
||||
pub fn with_timestamp(mut self, timestamp: Duration) -> Self {
|
||||
self.timestamp = Some(timestamp);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn timestamp_ms(&self) -> Option<u128> {
|
||||
self.timestamp_ms
|
||||
pub fn timestamp(&self) -> Option<Duration> {
|
||||
self.timestamp
|
||||
}
|
||||
|
||||
pub fn contains(&self, line: u32) -> bool {
|
||||
|
@ -2,12 +2,14 @@ mod file_ownership;
|
||||
mod hunk;
|
||||
mod ownership;
|
||||
|
||||
use anyhow::Result;
|
||||
use std::time::Duration;
|
||||
|
||||
pub use file_ownership::OwnershipClaim;
|
||||
pub use hunk::{Hunk, HunkHash};
|
||||
pub use ownership::{reconcile_claims, BranchOwnershipClaims};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::time::duration_serializer;
|
||||
use crate::{git, id::Id};
|
||||
|
||||
pub type BranchId = Id<Branch>;
|
||||
@ -25,16 +27,10 @@ pub struct Branch {
|
||||
pub upstream: Option<git::RemoteRefname>,
|
||||
// upstream_head is the last commit on we've pushed to the upstream branch
|
||||
pub upstream_head: Option<git::Oid>,
|
||||
#[serde(
|
||||
serialize_with = "serialize_u128",
|
||||
deserialize_with = "deserialize_u128"
|
||||
)]
|
||||
pub created_timestamp_ms: u128,
|
||||
#[serde(
|
||||
serialize_with = "serialize_u128",
|
||||
deserialize_with = "deserialize_u128"
|
||||
)]
|
||||
pub updated_timestamp_ms: u128,
|
||||
#[serde(rename = "created_timestamp_ms", with = "duration_serializer")]
|
||||
pub created_at: Duration,
|
||||
#[serde(rename = "updated_timestamp_ms", with = "duration_serializer")]
|
||||
pub updated_at: Duration,
|
||||
/// tree is the last git tree written to a session, or merge base tree if this is new. use this for delta calculation from the session data
|
||||
pub tree: git::Oid,
|
||||
/// head is id of the last "virtual" commit in this branch
|
||||
@ -47,22 +43,6 @@ pub struct Branch {
|
||||
pub selected_for_changes: Option<i64>,
|
||||
}
|
||||
|
||||
fn serialize_u128<S>(x: &u128, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
s.serialize_str(&x.to_string())
|
||||
}
|
||||
|
||||
fn deserialize_u128<'de, D>(d: D) -> Result<u128, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(d)?;
|
||||
let x: u128 = s.parse().map_err(serde::de::Error::custom)?;
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
impl Branch {
|
||||
pub fn refname(&self) -> git::VirtualRefname {
|
||||
self.into()
|
||||
@ -87,144 +67,3 @@ pub struct BranchCreateRequest {
|
||||
pub order: Option<usize>,
|
||||
pub selected_for_changes: Option<bool>,
|
||||
}
|
||||
|
||||
impl Branch {
|
||||
pub fn from_reader(reader: &crate::reader::Reader<'_>) -> Result<Self, crate::reader::Error> {
|
||||
let results = reader.batch(&[
|
||||
"id",
|
||||
"meta/name",
|
||||
"meta/notes",
|
||||
"meta/applied",
|
||||
"meta/order",
|
||||
"meta/upstream",
|
||||
"meta/upstream_head",
|
||||
"meta/tree",
|
||||
"meta/head",
|
||||
"meta/created_timestamp_ms",
|
||||
"meta/updated_timestamp_ms",
|
||||
"meta/ownership",
|
||||
"meta/selected_for_changes",
|
||||
])?;
|
||||
|
||||
let id: String = results[0].clone()?.try_into()?;
|
||||
let id: BranchId = id.parse().map_err(|e| {
|
||||
crate::reader::Error::Io(
|
||||
std::io::Error::new(std::io::ErrorKind::Other, format!("id: {}", e)).into(),
|
||||
)
|
||||
})?;
|
||||
let name: String = results[1].clone()?.try_into()?;
|
||||
|
||||
let notes: String = match results[2].clone() {
|
||||
Ok(notes) => Ok(notes.try_into()?),
|
||||
Err(crate::reader::Error::NotFound) => Ok(String::new()),
|
||||
Err(e) => Err(e),
|
||||
}?;
|
||||
|
||||
let applied = match results[3].clone() {
|
||||
Ok(applied) => applied.try_into(),
|
||||
_ => Ok(false),
|
||||
}
|
||||
.unwrap_or(false);
|
||||
|
||||
let order: usize = match results[4].clone() {
|
||||
Ok(order) => Ok(order.try_into()?),
|
||||
Err(crate::reader::Error::NotFound) => Ok(0),
|
||||
Err(e) => Err(e),
|
||||
}?;
|
||||
|
||||
let upstream = match results[5].clone() {
|
||||
Ok(crate::reader::Content::UTF8(upstream)) => {
|
||||
if upstream.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
upstream
|
||||
.parse::<git::RemoteRefname>()
|
||||
.map(Some)
|
||||
.map_err(|e| {
|
||||
crate::reader::Error::Io(
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("meta/upstream: {}", e),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
Ok(_) | Err(crate::reader::Error::NotFound) => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
}?;
|
||||
|
||||
let upstream_head = match results[6].clone() {
|
||||
Ok(crate::reader::Content::UTF8(upstream_head)) => {
|
||||
upstream_head.parse().map(Some).map_err(|e| {
|
||||
crate::reader::Error::Io(
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("meta/upstream_head: {}", e),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})
|
||||
}
|
||||
Ok(_) | Err(crate::reader::Error::NotFound) => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
}?;
|
||||
|
||||
let tree: String = results[7].clone()?.try_into()?;
|
||||
let head: String = results[8].clone()?.try_into()?;
|
||||
let created_timestamp_ms = results[9].clone()?.try_into()?;
|
||||
let updated_timestamp_ms = results[10].clone()?.try_into()?;
|
||||
|
||||
let ownership_string: String = results[11].clone()?.try_into()?;
|
||||
let ownership = ownership_string.parse().map_err(|e| {
|
||||
crate::reader::Error::Io(
|
||||
std::io::Error::new(std::io::ErrorKind::Other, format!("meta/ownership: {}", e))
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let selected_for_changes = match results[12].clone() {
|
||||
Ok(raw_ts) => {
|
||||
let ts = raw_ts.try_into().map_err(|e| {
|
||||
crate::reader::Error::Io(
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("meta/selected_for_changes: {}", e),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
Ok(Some(ts))
|
||||
}
|
||||
Err(crate::reader::Error::NotFound) => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
}?;
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
name,
|
||||
notes,
|
||||
applied,
|
||||
upstream,
|
||||
upstream_head,
|
||||
tree: tree.parse().map_err(|e| {
|
||||
crate::reader::Error::Io(
|
||||
std::io::Error::new(std::io::ErrorKind::Other, format!("meta/tree: {}", e))
|
||||
.into(),
|
||||
)
|
||||
})?,
|
||||
head: head.parse().map_err(|e| {
|
||||
crate::reader::Error::Io(
|
||||
std::io::Error::new(std::io::ErrorKind::Other, format!("meta/head: {}", e))
|
||||
.into(),
|
||||
)
|
||||
})?,
|
||||
created_timestamp_ms,
|
||||
updated_timestamp_ms,
|
||||
ownership,
|
||||
order,
|
||||
selected_for_changes,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
snapshot::Snapshot,
|
||||
},
|
||||
};
|
||||
use std::{collections::HashMap, path::Path, sync::Arc};
|
||||
use std::{collections::HashMap, path::Path, sync::Arc, time::SystemTime};
|
||||
|
||||
use anyhow::Context;
|
||||
use tokio::{sync::Semaphore, task::JoinHandle};
|
||||
@ -930,10 +930,10 @@ impl ControllerInner {
|
||||
.map_err(errors::FetchFromTargetError::Remote)
|
||||
{
|
||||
Ok(()) => projects::FetchResult::Fetched {
|
||||
timestamp: std::time::SystemTime::now(),
|
||||
timestamp: SystemTime::now(),
|
||||
},
|
||||
Err(error) => projects::FetchResult::Error {
|
||||
timestamp: std::time::SystemTime::now(),
|
||||
timestamp: SystemTime::now(),
|
||||
error: error.to_string(),
|
||||
},
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::path::Path;
|
||||
use std::{path::Path, time::Duration};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use bstr::BString;
|
||||
@ -25,7 +25,7 @@ pub struct RemoteBranch {
|
||||
pub sha: git::Oid,
|
||||
pub name: git::Refname,
|
||||
pub upstream: Option<git::RemoteRefname>,
|
||||
pub last_commit_timestamp_ms: Option<u128>,
|
||||
pub last_commit_at: Option<Duration>,
|
||||
pub last_commit_author: Option<String>,
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ pub struct RemoteCommit {
|
||||
pub id: String,
|
||||
#[serde(serialize_with = "crate::serde::as_string_lossy")]
|
||||
pub description: BString,
|
||||
pub created_at: u128,
|
||||
pub created_at: Duration,
|
||||
pub author: Author,
|
||||
}
|
||||
|
||||
@ -128,12 +128,7 @@ pub fn branch_to_remote_branch(branch: &git::Branch) -> Result<Option<RemoteBran
|
||||
None
|
||||
},
|
||||
name,
|
||||
last_commit_timestamp_ms: commit
|
||||
.time()
|
||||
.seconds()
|
||||
.try_into()
|
||||
.map(|t: u128| t * 1000)
|
||||
.ok(),
|
||||
last_commit_at: Some(commit.time()),
|
||||
last_commit_author: commit
|
||||
.author()
|
||||
.name()
|
||||
@ -185,7 +180,7 @@ pub fn commit_to_remote_commit(commit: &git::Commit) -> RemoteCommit {
|
||||
RemoteCommit {
|
||||
id: commit.id().to_string(),
|
||||
description: commit.message().to_owned(),
|
||||
created_at: commit.time().seconds().try_into().unwrap(),
|
||||
created_at: commit.time(),
|
||||
author: commit.author().into(),
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::borrow::Borrow;
|
||||
#[cfg(target_family = "unix")]
|
||||
use std::os::unix::prelude::PermissionsExt;
|
||||
use std::time::SystemTime;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
hash::Hash,
|
||||
@ -65,7 +65,7 @@ pub struct VirtualBranch {
|
||||
pub upstream_name: Option<String>, // the upstream branch where this branch will push to on next push
|
||||
pub base_current: bool, // is this vbranch based on the current base branch? if false, this needs to be manually merged with conflicts
|
||||
pub ownership: BranchOwnershipClaims,
|
||||
pub updated_at: u128,
|
||||
pub updated_at: Duration,
|
||||
pub selected_for_changes: bool,
|
||||
pub head: git::Oid,
|
||||
}
|
||||
@ -91,7 +91,7 @@ pub struct VirtualBranchCommit {
|
||||
pub id: git::Oid,
|
||||
#[serde(serialize_with = "crate::serde::as_string_lossy")]
|
||||
pub description: BString,
|
||||
pub created_at: u128,
|
||||
pub created_at: Duration,
|
||||
pub author: Author,
|
||||
pub is_remote: bool,
|
||||
pub files: Vec<VirtualBranchFile>,
|
||||
@ -117,7 +117,7 @@ pub struct VirtualBranchFile {
|
||||
pub id: String,
|
||||
pub path: PathBuf,
|
||||
pub hunks: Vec<VirtualBranchHunk>,
|
||||
pub modified_at: u128,
|
||||
pub modified_at: Duration,
|
||||
pub conflicted: bool,
|
||||
pub binary: bool,
|
||||
pub large: bool,
|
||||
@ -137,7 +137,7 @@ pub struct VirtualBranchHunk {
|
||||
pub id: String,
|
||||
#[serde(serialize_with = "crate::serde::as_string_lossy")]
|
||||
pub diff: BString,
|
||||
pub modified_at: u128,
|
||||
pub modified_at: Duration,
|
||||
pub file_path: PathBuf,
|
||||
#[serde(serialize_with = "crate::serde::hash_to_hex")]
|
||||
pub hash: HunkHash,
|
||||
@ -903,7 +903,7 @@ pub fn list_virtual_branches(
|
||||
conflicted: conflicts::is_resolving(project_repository),
|
||||
base_current,
|
||||
ownership: branch.ownership,
|
||||
updated_at: branch.updated_timestamp_ms,
|
||||
updated_at: branch.updated_at,
|
||||
selected_for_changes: branch.selected_for_changes == Some(max_selected_for_changes),
|
||||
head: branch.head,
|
||||
};
|
||||
@ -996,7 +996,7 @@ fn commit_to_vbranch_commit(
|
||||
is_integrated: bool,
|
||||
is_remote: bool,
|
||||
) -> Result<VirtualBranchCommit> {
|
||||
let timestamp = u128::try_from(commit.time().seconds())?;
|
||||
let timestamp = commit.time();
|
||||
let signature = commit.author();
|
||||
let message = commit.message().to_owned();
|
||||
|
||||
@ -1007,7 +1007,7 @@ fn commit_to_vbranch_commit(
|
||||
|
||||
let commit = VirtualBranchCommit {
|
||||
id: commit.id(),
|
||||
created_at: timestamp * 1000,
|
||||
created_at: timestamp,
|
||||
author: Author::from(signature),
|
||||
description: message,
|
||||
is_remote,
|
||||
@ -1092,7 +1092,7 @@ pub fn create_virtual_branch(
|
||||
.unwrap_or(&"Virtual branch".to_string()),
|
||||
);
|
||||
|
||||
let now = crate::time::now_ms();
|
||||
let now = crate::time::now();
|
||||
|
||||
let mut branch = Branch {
|
||||
id: BranchId::generate(),
|
||||
@ -1103,8 +1103,8 @@ pub fn create_virtual_branch(
|
||||
upstream_head: None,
|
||||
tree: tree.id(),
|
||||
head: default_target.sha,
|
||||
created_timestamp_ms: now,
|
||||
updated_timestamp_ms: now,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
ownership: BranchOwnershipClaims::default(),
|
||||
order,
|
||||
selected_for_changes,
|
||||
@ -1525,10 +1525,10 @@ fn set_ownership(
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MTimeCache(HashMap<PathBuf, u128>);
|
||||
struct MTimeCache(HashMap<PathBuf, Duration>);
|
||||
|
||||
impl MTimeCache {
|
||||
fn mtime_by_path<P: AsRef<Path>>(&mut self, path: P) -> u128 {
|
||||
fn mtime_by_path<P: AsRef<Path>>(&mut self, path: P) -> Duration {
|
||||
let path = path.as_ref();
|
||||
|
||||
if let Some(mtime) = self.0.get(path) {
|
||||
@ -1547,7 +1547,7 @@ impl MTimeCache {
|
||||
},
|
||||
)
|
||||
.duration_since(time::UNIX_EPOCH)
|
||||
.map_or(0, |d| d.as_millis());
|
||||
.unwrap_or(Duration::ZERO);
|
||||
self.0.insert(path.into(), mtime);
|
||||
mtime
|
||||
}
|
||||
@ -1805,7 +1805,7 @@ fn get_applied_status(
|
||||
return None; // Defer allocation to unclaimed hunks processing
|
||||
}
|
||||
if claimed_hunk.eq(&Hunk::from(git_diff_hunk)) {
|
||||
let timestamp = claimed_hunk.timestamp_ms().unwrap_or(mtime);
|
||||
let timestamp = claimed_hunk.timestamp().unwrap_or(mtime);
|
||||
diffs_by_branch
|
||||
.entry(branch.id)
|
||||
.or_default()
|
||||
@ -1830,7 +1830,7 @@ fn get_applied_status(
|
||||
let updated_hunk = Hunk {
|
||||
start: git_diff_hunk.new_start,
|
||||
end: git_diff_hunk.new_start + git_diff_hunk.new_lines,
|
||||
timestamp_ms: Some(mtime),
|
||||
timestamp: Some(mtime),
|
||||
hash: Some(hash),
|
||||
locked_to: git_diff_hunk.locked_to.to_vec(),
|
||||
};
|
||||
@ -1956,7 +1956,11 @@ fn virtual_hunks_into_virtual_files(
|
||||
let conflicted =
|
||||
conflicts::is_conflicting(project_repository, Some(&id)).unwrap_or(false);
|
||||
let binary = hunks.iter().any(|h| h.binary);
|
||||
let modified_at = hunks.iter().map(|h| h.modified_at).max().unwrap_or(0);
|
||||
let modified_at = hunks
|
||||
.iter()
|
||||
.map(|h| h.modified_at)
|
||||
.max()
|
||||
.unwrap_or(Duration::ZERO);
|
||||
debug_assert!(hunks.iter().all(|hunk| hunk.file_path == path));
|
||||
VirtualBranchFile {
|
||||
id,
|
||||
@ -4052,7 +4056,7 @@ pub fn create_virtual_branch_from_branch(
|
||||
.any(|b| b.selected_for_changes.is_some()))
|
||||
.then_some(chrono::Utc::now().timestamp_millis());
|
||||
|
||||
let now = crate::time::now_ms();
|
||||
let now = crate::time::now();
|
||||
|
||||
// only set upstream if it's not the default target
|
||||
let upstream_branch = match upstream {
|
||||
@ -4118,8 +4122,8 @@ pub fn create_virtual_branch_from_branch(
|
||||
upstream: upstream_branch,
|
||||
tree: head_commit_tree.id(),
|
||||
head: head_commit.id(),
|
||||
created_timestamp_ms: now,
|
||||
updated_timestamp_ms: now,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
ownership,
|
||||
order,
|
||||
selected_for_changes,
|
||||
|
@ -14,29 +14,29 @@ async fn should_update_last_fetched() {
|
||||
.unwrap();
|
||||
|
||||
let before_fetch = controller.get_base_branch_data(project_id).await.unwrap();
|
||||
assert!(before_fetch.last_fetched_ms.is_none());
|
||||
assert!(before_fetch.last_fetched_at.is_none());
|
||||
|
||||
let fetch = controller
|
||||
.fetch_from_target(project_id, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(fetch.last_fetched_ms.is_some());
|
||||
assert!(fetch.last_fetched_at.is_some());
|
||||
|
||||
let after_fetch = controller.get_base_branch_data(project_id).await.unwrap();
|
||||
assert!(after_fetch.last_fetched_ms.is_some());
|
||||
assert_eq!(fetch.last_fetched_ms, after_fetch.last_fetched_ms);
|
||||
assert!(after_fetch.last_fetched_at.is_some());
|
||||
assert_eq!(fetch.last_fetched_at, after_fetch.last_fetched_at);
|
||||
|
||||
let second_fetch = controller
|
||||
.fetch_from_target(project_id, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(second_fetch.last_fetched_ms.is_some());
|
||||
assert_ne!(fetch.last_fetched_ms, second_fetch.last_fetched_ms);
|
||||
assert!(second_fetch.last_fetched_at.is_some());
|
||||
assert_ne!(fetch.last_fetched_at, second_fetch.last_fetched_at);
|
||||
|
||||
let after_second_fetch = controller.get_base_branch_data(project_id).await.unwrap();
|
||||
assert!(after_second_fetch.last_fetched_ms.is_some());
|
||||
assert!(after_second_fetch.last_fetched_at.is_some());
|
||||
assert_eq!(
|
||||
second_fetch.last_fetched_ms,
|
||||
after_second_fetch.last_fetched_ms
|
||||
second_fetch.last_fetched_at,
|
||||
after_second_fetch.last_fetched_at
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use gitbutler_core::virtual_branches::branch::Hunk;
|
||||
|
||||
#[test]
|
||||
@ -24,7 +26,7 @@ fn parse_with_hash() {
|
||||
fn parse_with_timestamp() {
|
||||
assert_eq!(
|
||||
"2-3--123".parse::<Hunk>().unwrap(),
|
||||
Hunk::new(2, 3, None, Some(123)).unwrap()
|
||||
Hunk::new(2, 3, None, Some(Duration::from_secs(123))).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
@ -37,7 +39,9 @@ fn parse_invalid_2() {
|
||||
fn to_string_no_hash() {
|
||||
assert_eq!(
|
||||
"1-2--123",
|
||||
Hunk::new(1, 2, None, Some(123)).unwrap().to_string()
|
||||
Hunk::new(1, 2, None, Some(Duration::from_secs(123)))
|
||||
.unwrap()
|
||||
.to_string()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -17,14 +17,14 @@ fn reconcile_ownership_simple() {
|
||||
start: 1,
|
||||
end: 3,
|
||||
hash: Some(Hunk::hash("1,3")),
|
||||
timestamp_ms: None,
|
||||
timestamp: None,
|
||||
locked_to: vec![],
|
||||
},
|
||||
Hunk {
|
||||
start: 4,
|
||||
end: 6,
|
||||
hash: Some(Hunk::hash("4,6")),
|
||||
timestamp_ms: None,
|
||||
timestamp: None,
|
||||
locked_to: vec![],
|
||||
},
|
||||
],
|
||||
@ -42,7 +42,7 @@ fn reconcile_ownership_simple() {
|
||||
start: 7,
|
||||
end: 9,
|
||||
hash: Some(Hunk::hash("7,9")),
|
||||
timestamp_ms: None,
|
||||
timestamp: None,
|
||||
locked_to: vec![],
|
||||
}],
|
||||
}],
|
||||
@ -58,14 +58,14 @@ fn reconcile_ownership_simple() {
|
||||
start: 4,
|
||||
end: 6,
|
||||
hash: Some(Hunk::hash("4,6")),
|
||||
timestamp_ms: None,
|
||||
timestamp: None,
|
||||
locked_to: vec![],
|
||||
},
|
||||
Hunk {
|
||||
start: 7,
|
||||
end: 9,
|
||||
hash: Some(Hunk::hash("9,7")),
|
||||
timestamp_ms: None,
|
||||
timestamp: None,
|
||||
locked_to: vec![],
|
||||
},
|
||||
],
|
||||
@ -84,7 +84,7 @@ fn reconcile_ownership_simple() {
|
||||
start: 1,
|
||||
end: 3,
|
||||
hash: Some(Hunk::hash("1,3")),
|
||||
timestamp_ms: None,
|
||||
timestamp: None,
|
||||
locked_to: vec![],
|
||||
},],
|
||||
}],
|
||||
@ -101,14 +101,14 @@ fn reconcile_ownership_simple() {
|
||||
start: 4,
|
||||
end: 6,
|
||||
hash: Some(Hunk::hash("4,6")),
|
||||
timestamp_ms: None,
|
||||
timestamp: None,
|
||||
locked_to: vec![],
|
||||
},
|
||||
Hunk {
|
||||
start: 7,
|
||||
end: 9,
|
||||
hash: Some(Hunk::hash("9,7")),
|
||||
timestamp_ms: None,
|
||||
timestamp: None,
|
||||
locked_to: vec![],
|
||||
},
|
||||
],
|
||||
|
@ -1,4 +1,7 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::{
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use gitbutler_core::virtual_branches;
|
||||
@ -25,8 +28,8 @@ fn new_test_branch() -> virtual_branches::branch::Branch {
|
||||
.unwrap(),
|
||||
),
|
||||
upstream_head: None,
|
||||
created_timestamp_ms: TEST_INDEX.load(Ordering::Relaxed) as u128,
|
||||
updated_timestamp_ms: (TEST_INDEX.load(Ordering::Relaxed) + 100) as u128,
|
||||
created_at: Duration::from_millis(TEST_INDEX.load(Ordering::Relaxed) as u64),
|
||||
updated_at: Duration::from_millis((TEST_INDEX.load(Ordering::Relaxed) + 100) as u64),
|
||||
head: format!(
|
||||
"0123456789abcdef0123456789abcdef0123456{}",
|
||||
TEST_INDEX.load(Ordering::Relaxed)
|
||||
|
Loading…
Reference in New Issue
Block a user