mirror of
https://github.com/oliverbooth/fdup.git
synced 2024-11-10 05:35:42 +00:00
Compare commits
No commits in common. "c65d6ed4147adb910b11d4fbe5e616efb452c71c" and "d2ca59e174ca9cd3392c46d0249a3023089a1188" have entirely different histories.
c65d6ed414
...
d2ca59e174
66
.github/workflows/prerelease.yml
vendored
66
.github/workflows/prerelease.yml
vendored
@ -1,66 +0,0 @@
|
|||||||
name: Tagged Pre-Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v[0-9]+.[0-9]+.[0-9]+-*"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
name: "Tagged Pre-Release"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v3
|
|
||||||
with:
|
|
||||||
dotnet-version: 8.0.x
|
|
||||||
|
|
||||||
- name: Restore dependencies
|
|
||||||
run: dotnet restore
|
|
||||||
|
|
||||||
- name: Get version from tag
|
|
||||||
id: get_version
|
|
||||||
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v}
|
|
||||||
|
|
||||||
- name: Build and Publish
|
|
||||||
run: |
|
|
||||||
dotnet publish -c Release -p PublishSingleFile=true -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} -o ./publish/win-x64 -r win-x64
|
|
||||||
dotnet publish -c Release -p PublishSingleFile=true -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} -o ./publish/win-x86 -r win-x86
|
|
||||||
dotnet publish -c Release -p PublishSingleFile=true -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} -o ./publish/linux-x64 -r linux-x64
|
|
||||||
dotnet publish -c Release -p PublishSingleFile=true -p:VersionSuffix='prerelease' -p:BuildNumber=${{ github.run_number }} -o ./publish/osx-x64 -r osx-x64
|
|
||||||
|
|
||||||
- name: Upload build artifacts
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: publish
|
|
||||||
path: publish/
|
|
||||||
|
|
||||||
- name: Create Release
|
|
||||||
id: create_release
|
|
||||||
uses: "marvinpinto/action-automatic-releases@latest"
|
|
||||||
with:
|
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
prerelease: false
|
|
||||||
|
|
||||||
- name: Upload Release Assets
|
|
||||||
id: upload-release-assets
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
with:
|
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
|
||||||
asset_path: |
|
|
||||||
./publish/win-x64/fdup.exe
|
|
||||||
./publish/win-x86/fdup.exe
|
|
||||||
./publish/linux-x64/fdup
|
|
||||||
./publish/osx-x64/fdup
|
|
||||||
asset_name: |
|
|
||||||
fdup-${{ steps.get_version.outputs.VERSION }}-win-x64.exe
|
|
||||||
fdup-${{ steps.get_version.outputs.VERSION }}-win-x86.exe
|
|
||||||
fdup-${{ steps.get_version.outputs.VERSION }}-linux_x64
|
|
||||||
fdup-${{ steps.get_version.outputs.VERSION }}-macos_x64
|
|
||||||
asset_content_type: application/octet-stream
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
66
.github/workflows/release.yml
vendored
66
.github/workflows/release.yml
vendored
@ -1,66 +0,0 @@
|
|||||||
name: Tagged Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v[0-9]+.[0-9]+.[0-9]+"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
name: "Tagged Release"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v3
|
|
||||||
with:
|
|
||||||
dotnet-version: 8.0.x
|
|
||||||
|
|
||||||
- name: Restore dependencies
|
|
||||||
run: dotnet restore
|
|
||||||
|
|
||||||
- name: Get version from tag
|
|
||||||
id: get_version
|
|
||||||
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v}
|
|
||||||
|
|
||||||
- name: Build and Publish
|
|
||||||
run: |
|
|
||||||
dotnet publish -c Release -p PublishSingleFile=true -o ./publish/win-x64 -r win-x64
|
|
||||||
dotnet publish -c Release -p PublishSingleFile=true -o ./publish/win-x86 -r win-x86
|
|
||||||
dotnet publish -c Release -p PublishSingleFile=true -o ./publish/linux-x64 -r linux-x64
|
|
||||||
dotnet publish -c Release -p PublishSingleFile=true -o ./publish/osx-x64 -r osx-x64
|
|
||||||
|
|
||||||
- name: Upload build artifacts
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: publish
|
|
||||||
path: publish/
|
|
||||||
|
|
||||||
- name: Create Release
|
|
||||||
id: create_release
|
|
||||||
uses: "marvinpinto/action-automatic-releases@latest"
|
|
||||||
with:
|
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
prerelease: false
|
|
||||||
|
|
||||||
- name: Upload Release Assets
|
|
||||||
id: upload-release-assets
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
with:
|
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
|
||||||
asset_path: |
|
|
||||||
./publish/win-x64/fdup.exe
|
|
||||||
./publish/win-x86/fdup.exe
|
|
||||||
./publish/linux-x64/fdup
|
|
||||||
./publish/osx-x64/fdup
|
|
||||||
asset_name: |
|
|
||||||
fdup-${{ steps.get_version.outputs.VERSION }}-win-x64.exe
|
|
||||||
fdup-${{ steps.get_version.outputs.VERSION }}-win-x86.exe
|
|
||||||
fdup-${{ steps.get_version.outputs.VERSION }}-linux_x64
|
|
||||||
fdup-${{ steps.get_version.outputs.VERSION }}-macos_x64
|
|
||||||
asset_content_type: application/octet-stream
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -6,7 +6,7 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AssemblyName>fdup</AssemblyName>
|
<AssemblyName>fdup</AssemblyName>
|
||||||
<VersionPrefix>1.2.0</VersionPrefix>
|
<VersionPrefix>1.1.0</VersionPrefix>
|
||||||
<Authors>Oliver Booth</Authors>
|
<Authors>Oliver Booth</Authors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Security;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
@ -8,7 +10,7 @@ namespace FindDuplicates;
|
|||||||
|
|
||||||
internal sealed class ListCommand : AsyncCommand<ListSettings>
|
internal sealed class ListCommand : AsyncCommand<ListSettings>
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<string, ConcurrentBag<FileInfo>> _fileHashMap = new();
|
private readonly ConcurrentDictionary<string, List<FileInfo>> _fileHashMap = new();
|
||||||
|
|
||||||
public override async Task<int> ExecuteAsync(CommandContext context, ListSettings settings)
|
public override async Task<int> ExecuteAsync(CommandContext context, ListSettings settings)
|
||||||
{
|
{
|
||||||
@ -29,7 +31,7 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
AnsiConsole.WriteLine();
|
AnsiConsole.WriteLine();
|
||||||
|
|
||||||
int duplicates = 0;
|
int duplicates = 0;
|
||||||
foreach ((string hash, ConcurrentBag<FileInfo> files) in _fileHashMap)
|
foreach ((string hash, List<FileInfo> files) in _fileHashMap)
|
||||||
{
|
{
|
||||||
int fileCount = files.Count;
|
int fileCount = files.Count;
|
||||||
|
|
||||||
@ -89,7 +91,18 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
if (relativePath != ".")
|
if (relativePath != ".")
|
||||||
AnsiConsole.MarkupLineInterpolated($"Searching [cyan]{relativePath}[/]");
|
AnsiConsole.MarkupLineInterpolated($"Searching [cyan]{relativePath}[/]");
|
||||||
|
|
||||||
AddChildDirectories(settings, currentDirectory, directoryStack);
|
if (settings.Recursive)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (DirectoryInfo childDirectory in currentDirectory.EnumerateDirectories())
|
||||||
|
directoryStack.Push(childDirectory);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"[red]Error:[/] {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -97,7 +110,7 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
{
|
{
|
||||||
string relativeFilePath = Path.GetRelativePath(inputDirectory.FullName, file.FullName);
|
string relativeFilePath = Path.GetRelativePath(inputDirectory.FullName, file.FullName);
|
||||||
AnsiConsole.MarkupLineInterpolated($"Checking hash for [cyan]{relativeFilePath}[/]");
|
AnsiConsole.MarkupLineInterpolated($"Checking hash for [cyan]{relativeFilePath}[/]");
|
||||||
tasks.Add(Task.Run(() => ProcessFile(file, settings)));
|
tasks.Add(Task.Run(() => ProcessFile(file)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -107,7 +120,7 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessFile(FileInfo file, ListSettings settings)
|
private void ProcessFile(FileInfo file)
|
||||||
{
|
{
|
||||||
Span<byte> buffer = stackalloc byte[64];
|
Span<byte> buffer = stackalloc byte[64];
|
||||||
try
|
try
|
||||||
@ -116,10 +129,12 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
using BufferedStream bufferedStream = new BufferedStream(stream, 1048576 /* 1MB */);
|
using BufferedStream bufferedStream = new BufferedStream(stream, 1048576 /* 1MB */);
|
||||||
SHA512.HashData(bufferedStream, buffer);
|
SHA512.HashData(bufferedStream, buffer);
|
||||||
string hash = ByteSpanToString(buffer);
|
string hash = ByteSpanToString(buffer);
|
||||||
if (settings.Verbose)
|
Trace.WriteLine($"{file.FullName}: {hash}");
|
||||||
AnsiConsole.WriteLine($"{file.FullName} ->\n {hash}");
|
|
||||||
|
|
||||||
ConcurrentBag<FileInfo> cache = _fileHashMap.GetOrAdd(hash, _ => []);
|
if (!_fileHashMap.TryGetValue(hash, out List<FileInfo>? cache))
|
||||||
|
_fileHashMap[hash] = cache = new List<FileInfo>();
|
||||||
|
|
||||||
|
lock (cache)
|
||||||
cache.Add(file);
|
cache.Add(file);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -128,22 +143,6 @@ internal sealed class ListCommand : AsyncCommand<ListSettings>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddChildDirectories(ListSettings settings, DirectoryInfo directory, Stack<DirectoryInfo> stack)
|
|
||||||
{
|
|
||||||
if (!settings.Recursive)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (DirectoryInfo childDirectory in directory.EnumerateDirectories())
|
|
||||||
stack.Push(childDirectory);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
AnsiConsole.MarkupLineInterpolated($"[red]Error:[/] {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ByteSpanToString(ReadOnlySpan<byte> buffer)
|
private static string ByteSpanToString(ReadOnlySpan<byte> buffer)
|
||||||
{
|
{
|
||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
|
@ -5,18 +5,12 @@ namespace FindDuplicates;
|
|||||||
|
|
||||||
internal sealed class ListSettings : CommandSettings
|
internal sealed class ListSettings : CommandSettings
|
||||||
{
|
{
|
||||||
[CommandArgument(0, "[path]")]
|
[CommandArgument(0, "<path>")]
|
||||||
[Description("The path to search. Defaults to the current directory.")]
|
[Description("The path to search.")]
|
||||||
[DefaultValue(".")]
|
public string InputPath { get; set; } = string.Empty;
|
||||||
public string InputPath { get; set; } = ".";
|
|
||||||
|
|
||||||
[CommandOption("-r|--recursive")]
|
[CommandOption("-r|--recursive")]
|
||||||
[Description("When this flag is set, the directory will be scanned recursively. This may take longer.")]
|
[Description("When this flag is set, the directory will be scanned recursively. This may take longer.")]
|
||||||
[DefaultValue(false)]
|
[DefaultValue(false)]
|
||||||
public bool Recursive { get; set; } = false;
|
public bool Recursive { get; set; } = false;
|
||||||
|
|
||||||
[CommandOption("--verbose")]
|
|
||||||
[Description("Enable verbose output.")]
|
|
||||||
[DefaultValue(false)]
|
|
||||||
public bool Verbose { get; set; } = false;
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user