[PR #989] [MERGED] Add support for empty directory entries in archives #1409

Open
opened 2026-01-29 22:20:25 +00:00 by claunia · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/adamhathcock/sharpcompress/pull/989
Author: @Copilot
Created: 10/28/2025
Status: Merged
Merged: 10/28/2025
Merged by: @adamhathcock

Base: masterHead: copilot/add-support-empty-directories


📝 Commits (8)

  • fda1c2c Initial plan
  • d148f36 Add support for empty directory entries in archives
  • 084f81f Format code with CSharpier
  • a93a3f0 Address code review feedback - fix formatting
  • c8e4915 Final progress report
  • e88841b Add support for empty directory entries in archives
  • b4352fe Fix code formatting per CSharpier standards
  • b799f47 Merge branch 'master' into copilot/add-support-empty-directories

📊 Changes

19 files changed (+880 additions, -39 deletions)

View changed files

📝 src/SharpCompress/Archives/AbstractWritableArchive.cs (+21 -0)
📝 src/SharpCompress/Archives/GZip/GZipArchive.cs (+5 -0)
📝 src/SharpCompress/Archives/IWritableArchive.cs (+2 -0)
📝 src/SharpCompress/Archives/Tar/TarArchive.cs (+47 -19)
📝 src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs (+25 -4)
📝 src/SharpCompress/Archives/Zip/ZipArchive.cs (+44 -12)
📝 src/SharpCompress/Archives/Zip/ZipWritableArchiveEntry.cs (+25 -4)
📝 src/SharpCompress/Writers/AbstractWriter.cs (+14 -0)
📝 src/SharpCompress/Writers/GZip/GZipWriter.cs (+3 -0)
📝 src/SharpCompress/Writers/IWriter.cs (+6 -0)
📝 src/SharpCompress/Writers/IWriterExtensions.cs (+9 -0)
📝 src/SharpCompress/Writers/Tar/TarWriter.cs (+38 -0)
📝 src/SharpCompress/Writers/Zip/ZipWriter.cs (+69 -0)
tests/SharpCompress.Test/GZip/GZipArchiveDirectoryTests.cs (+19 -0)
tests/SharpCompress.Test/GZip/GZipWriterDirectoryTests.cs (+19 -0)
tests/SharpCompress.Test/Tar/TarArchiveDirectoryTests.cs (+112 -0)
tests/SharpCompress.Test/Tar/TarWriterDirectoryTests.cs (+164 -0)
tests/SharpCompress.Test/Zip/ZipArchiveDirectoryTests.cs (+112 -0)
tests/SharpCompress.Test/Zip/ZipWriterDirectoryTests.cs (+146 -0)

📄 Description

Add support for empty directory entries in archives

Summary

Successfully implemented support for adding empty directory entries to archives, addressing the feature request in issue #831.

Completed Tasks

  • Add WriteDirectory methods to IWriter interface for adding empty directory entries
  • Implement WriteDirectory in TarWriter (using EntryType.Directory)
  • Implement WriteDirectory in ZipWriter (using trailing '/' with zero size)
  • Handle GZipWriter (throw NotSupportedException as GZip doesn't support directories)
  • Add AddDirectoryEntry methods to IWritableArchive interface
  • Implement in writable archive implementations (Tar, Zip, GZip)
  • Add extension methods for convenience
  • Create comprehensive tests for both writers and writable archives
  • Verify all tests pass (24 new tests + all existing tests passing)
  • Run code formatter (CSharpier)
  • Request and address code review feedback
  • Run security scan (CodeQL) - No vulnerabilities found
  • Address formatting feedback

Implementation Details

Public API Additions:

  1. IWriter.WriteDirectory(string directoryName, DateTime? modificationTime) - synchronous
  2. IWriter.WriteDirectoryAsync(string directoryName, DateTime? modificationTime, CancellationToken) - asynchronous
  3. IWritableArchive.AddDirectoryEntry(string key, DateTime? modified) - add directory to archive

Format-Specific Behavior:

  • Tar: Uses EntryType.Directory with size 0, directories end with '/'
  • Zip: Uses trailing '/' with zero size and ZipCompressionMethod.None
  • GZip: Throws NotSupportedException (format limitation)

Backward Compatibility:
All existing functionality preserved. Changes are additive only.

Testing

  • 24 new tests covering directory creation, normalization, and edge cases
  • All existing tests pass (236+ tests total)
  • No security vulnerabilities detected by CodeQL
  • Code formatted per CSharpier standards

Usage Example

// Using Writers
using var writer = new TarWriter(stream, options);
writer.WriteDirectory("config", DateTime.Now);
writer.Write("config/app.json", fileStream, DateTime.Now);

// Using Writable Archives
using var archive = ZipArchive.Create();
archive.AddDirectoryEntry("logs", DateTime.Now);
archive.AddEntry("logs/app.log", logStream, true, logStream.Length, DateTime.Now);
archive.SaveTo(outputPath, CompressionType.Deflate);
Original prompt

This section details on the original issue you should resolve

<issue_title>Cannot Add Empty Directories to Archives</issue_title>
<issue_description>Description
Currently, SharpCompress lacks support for adding empty directories to archives. When using IWriter or IWritableArchive interfaces to add entries, there is no straightforward way to include empty directories, as the library appears to ignore directory entries that lack files. This limits use cases where the exact directory structure, including empty folders, is essential, such as preserving the file structure during compression or in deployment packages.

Motivation
Including empty directories is important in scenarios where the directory structure needs to be faithfully recreated after decompression. This is especially useful for:

  • Deployment packages where empty folders are placeholders for future files.
  • Compression of directories with empty folders that need to be preserved, ensuring consistency in archive content.
  • Development and build systems that rely on specific folder structures, including empty directories, to function correctly.

Proposed Solution
Consider adding a way to support empty directory entries in IWriter or IWritableArchive, allowing a Stream.Null or a specific method for empty directories, similar to:

archive.AddDirectoryEntry(relativePath);

or ensuring that calling AddEntry with Stream.Null and size: 0 explicitly adds an empty directory entry.

Alternative Workaround
Currently, the only workaround is to create a temporary file in each empty directory or use a marker file to ensure that the directory is not skipped, which is not ideal and requires post-extraction cleanup.

Thank you for considering this feature enhancement.</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/adamhathcock/sharpcompress/pull/989 **Author:** [@Copilot](https://github.com/apps/copilot-swe-agent) **Created:** 10/28/2025 **Status:** ✅ Merged **Merged:** 10/28/2025 **Merged by:** [@adamhathcock](https://github.com/adamhathcock) **Base:** `master` ← **Head:** `copilot/add-support-empty-directories` --- ### 📝 Commits (8) - [`fda1c2c`](https://github.com/adamhathcock/sharpcompress/commit/fda1c2cc793b7b69875c315228745df8f3e4aa5e) Initial plan - [`d148f36`](https://github.com/adamhathcock/sharpcompress/commit/d148f36e879f81cf11a6eb8b31022d30a2a95dcb) Add support for empty directory entries in archives - [`084f81f`](https://github.com/adamhathcock/sharpcompress/commit/084f81fc8d20617e376c8d6af10b8fdbd69bc8be) Format code with CSharpier - [`a93a3f0`](https://github.com/adamhathcock/sharpcompress/commit/a93a3f0598d754f1790d324dd0e8e192acb7930c) Address code review feedback - fix formatting - [`c8e4915`](https://github.com/adamhathcock/sharpcompress/commit/c8e4915f8eb393f431be732a97cd48ccd5cf030e) Final progress report - [`e88841b`](https://github.com/adamhathcock/sharpcompress/commit/e88841bdecd50b20e98a70fe1c7007a3bdd233c3) Add support for empty directory entries in archives - [`b4352fe`](https://github.com/adamhathcock/sharpcompress/commit/b4352fefa5df72173215aab1d2a6356d8c8c8ec7) Fix code formatting per CSharpier standards - [`b799f47`](https://github.com/adamhathcock/sharpcompress/commit/b799f479c47f83cdaee5e5d7d2c40aa7b1ac05ac) Merge branch 'master' into copilot/add-support-empty-directories ### 📊 Changes **19 files changed** (+880 additions, -39 deletions) <details> <summary>View changed files</summary> 📝 `src/SharpCompress/Archives/AbstractWritableArchive.cs` (+21 -0) 📝 `src/SharpCompress/Archives/GZip/GZipArchive.cs` (+5 -0) 📝 `src/SharpCompress/Archives/IWritableArchive.cs` (+2 -0) 📝 `src/SharpCompress/Archives/Tar/TarArchive.cs` (+47 -19) 📝 `src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs` (+25 -4) 📝 `src/SharpCompress/Archives/Zip/ZipArchive.cs` (+44 -12) 📝 `src/SharpCompress/Archives/Zip/ZipWritableArchiveEntry.cs` (+25 -4) 📝 `src/SharpCompress/Writers/AbstractWriter.cs` (+14 -0) 📝 `src/SharpCompress/Writers/GZip/GZipWriter.cs` (+3 -0) 📝 `src/SharpCompress/Writers/IWriter.cs` (+6 -0) 📝 `src/SharpCompress/Writers/IWriterExtensions.cs` (+9 -0) 📝 `src/SharpCompress/Writers/Tar/TarWriter.cs` (+38 -0) 📝 `src/SharpCompress/Writers/Zip/ZipWriter.cs` (+69 -0) ➕ `tests/SharpCompress.Test/GZip/GZipArchiveDirectoryTests.cs` (+19 -0) ➕ `tests/SharpCompress.Test/GZip/GZipWriterDirectoryTests.cs` (+19 -0) ➕ `tests/SharpCompress.Test/Tar/TarArchiveDirectoryTests.cs` (+112 -0) ➕ `tests/SharpCompress.Test/Tar/TarWriterDirectoryTests.cs` (+164 -0) ➕ `tests/SharpCompress.Test/Zip/ZipArchiveDirectoryTests.cs` (+112 -0) ➕ `tests/SharpCompress.Test/Zip/ZipWriterDirectoryTests.cs` (+146 -0) </details> ### 📄 Description Add support for empty directory entries in archives ## Summary Successfully implemented support for adding empty directory entries to archives, addressing the feature request in issue #831. ## Completed Tasks - [x] Add `WriteDirectory` methods to `IWriter` interface for adding empty directory entries - [x] Implement `WriteDirectory` in `TarWriter` (using EntryType.Directory) - [x] Implement `WriteDirectory` in `ZipWriter` (using trailing '/' with zero size) - [x] Handle `GZipWriter` (throw NotSupportedException as GZip doesn't support directories) - [x] Add `AddDirectoryEntry` methods to `IWritableArchive` interface - [x] Implement in writable archive implementations (Tar, Zip, GZip) - [x] Add extension methods for convenience - [x] Create comprehensive tests for both writers and writable archives - [x] Verify all tests pass (24 new tests + all existing tests passing) - [x] Run code formatter (CSharpier) - [x] Request and address code review feedback - [x] Run security scan (CodeQL) - No vulnerabilities found - [x] Address formatting feedback ## Implementation Details **Public API Additions:** 1. `IWriter.WriteDirectory(string directoryName, DateTime? modificationTime)` - synchronous 2. `IWriter.WriteDirectoryAsync(string directoryName, DateTime? modificationTime, CancellationToken)` - asynchronous 3. `IWritableArchive.AddDirectoryEntry(string key, DateTime? modified)` - add directory to archive **Format-Specific Behavior:** - **Tar**: Uses `EntryType.Directory` with size 0, directories end with '/' - **Zip**: Uses trailing '/' with zero size and `ZipCompressionMethod.None` - **GZip**: Throws `NotSupportedException` (format limitation) **Backward Compatibility:** All existing functionality preserved. Changes are additive only. ## Testing - 24 new tests covering directory creation, normalization, and edge cases - All existing tests pass (236+ tests total) - No security vulnerabilities detected by CodeQL - Code formatted per CSharpier standards ## Usage Example ```csharp // Using Writers using var writer = new TarWriter(stream, options); writer.WriteDirectory("config", DateTime.Now); writer.Write("config/app.json", fileStream, DateTime.Now); // Using Writable Archives using var archive = ZipArchive.Create(); archive.AddDirectoryEntry("logs", DateTime.Now); archive.AddEntry("logs/app.log", logStream, true, logStream.Length, DateTime.Now); archive.SaveTo(outputPath, CompressionType.Deflate); ``` <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Cannot Add Empty Directories to Archives</issue_title> > <issue_description>**Description** > Currently, SharpCompress lacks support for adding empty directories to archives. When using `IWriter` or `IWritableArchive `interfaces to add entries, there is no straightforward way to include empty directories, as the library appears to ignore directory entries that lack files. This limits use cases where the exact directory structure, including empty folders, is essential, such as preserving the file structure during compression or in deployment packages. > > **Motivation** > Including empty directories is important in scenarios where the directory structure needs to be faithfully recreated after decompression. This is especially useful for: > - Deployment packages where empty folders are placeholders for future files. > - Compression of directories with empty folders that need to be preserved, ensuring consistency in archive content. > - Development and build systems that rely on specific folder structures, including empty directories, to function correctly. > > **Proposed Solution** > Consider adding a way to support empty directory entries in `IWriter `or `IWritableArchive`, allowing a `Stream.Null` or a specific method for empty directories, similar to: > > `archive.AddDirectoryEntry(relativePath);` > > or ensuring that calling `AddEntry `with `Stream.Null `and size: 0 explicitly adds an empty directory entry. > > **Alternative Workaround** > Currently, the only workaround is to create a temporary file in each empty directory or use a marker file to ensure that the directory is not skipped, which is not ideal and requires post-extraction cleanup. > > Thank you for considering this feature enhancement.</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> - Fixes adamhathcock/sharpcompress#881 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
claunia added the pull-request label 2026-01-29 22:20:25 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/sharpcompress#1409