LmCast :: Stay tuned in

Show HN: DriveSync – fast Git-styled Google Drive sync CLI

Recorded: Sept. 8, 2026, 3:08 p.m.

Original Summarized

GitHub - scaleninja/drivesync: Superfast Google Drive CLI to sync files & folders · GitHub

Skip to content

Navigation MenuSign inAppearance settingsPlatformAI CODE CREATIONGitHub CopilotWrite better code with AIGitHub Copilot appDirect agents from issue to mergeMCP RegistryIntegrate external toolsDEVELOPER WORKFLOWSActionsAutomate any workflowCodespacesInstant dev environmentsIssuesPlan and track workCode ReviewManage code changesCode QualityEnforce quality at mergeAPPLICATION SECURITYGitHub Advanced SecurityFind and fix vulnerabilitiesCode securitySecure your code as you buildSecret protectionStop leaks before they startEXPLOREWhy GitHubDocumentationBlogChangelogMarketplaceView all featuresSolutionsBY COMPANY SIZEEnterprisesSmall and medium teamsStartupsNonprofitsBY USE CASEApp ModernizationDevSecOpsDevOpsCI/CDView all use casesBY INDUSTRYHealthcareFinancial servicesManufacturingGovernmentView all industriesView all solutionsResourcesEXPLORE BY TOPICAISoftware DevelopmentDevOpsSecurityView all topicsEXPLORE BY TYPECustomer storiesEvents & webinarsEbooks & reportsBusiness insightsGitHub SkillsSUPPORT & SERVICESDocumentationCustomer supportCommunity forumTrust centerPartnersView all resourcesOpen SourceCOMMUNITYGitHub SponsorsFund open source developersPROGRAMSSecurity LabMaintainer CommunityGitHub StarsArchive ProgramREPOSITORIESTopicsTrendingCollectionsEnterpriseENTERPRISE SOLUTIONSEnterprise platformAI-powered developer platformAVAILABLE ADD-ONSGitHub Advanced SecurityEnterprise-grade security featuresCopilot for BusinessEnterprise-grade AI featuresPremium SupportEnterprise-grade 24/7 supportPricingSearch/Sign inSign upAppearance settings

You signed in with another tab or window. Reload to refresh your session.
You signed out in another tab or window. Reload to refresh your session.
You switched accounts on another tab or window. Reload to refresh your session.

Dismiss alert

Uh oh!

There was an error while loading. Please reload this page.


scaleninja

/

drivesync

Public

Notifications
You must be signed in to change notification settings

Fork
0

Star
14

Code

Issues
0

Pull requests
0

Actions

Projects

Security and quality
0

Insights

Additional navigation options

Code

Issues

Pull requests

Actions

Projects

Security and quality

Insights

mainBranchesTagsGo to fileCodeOpen more actions menuLatest commit History20 Commits20 CommitsFolders and filesNameNameLast commit messageLast commit date.github/workflows.github/workflows  docsdocs  scriptsscripts  srcsrc  .gitignore.gitignore  Cargo.lockCargo.lock  Cargo.tomlCargo.toml  LICENSELICENSE  MakefileMakefile  README.mdREADME.md  SECURITY.mdSECURITY.md  View all filesRepository files navigationREADMEMIT licenseSecurityMore itemsDriveSync (dsync)
A small, fast command-line tool that keeps a local folder and a Google Drive folder in sync.
Push local changes up, pull remote changes down, or just see what differs. Written in Rust,
modelled on odeke-em/drive.

Home: https://scaleninja.com/drivesync/
Source: https://github.com/scaleninja/drivesync
License: MIT

dsync init ~/gdrive --remote-folder backups/lab --credentials ~/Downloads/client_secret.json
cd ~/gdrive
dsync diff # what differs?
dsync push # upload local changes (shows the plan, asks first)
dsync pull # download remote changes

Install
Homebrew (macOS and Linux):
brew install scaleninja/tap/drivesync
This adds the scaleninja/tap tap, so other
scaleninja tools then install with a plain brew install <name>. (If Homebrew refuses with an
"untrusted tap" error, run brew trust scaleninja/tap first.)
Or grab a prebuilt binary for Linux or macOS (x86_64 and arm64) from the
releases page:
case "$(uname -s)-$(uname -m)" in
Linux-x86_64) asset=linux-x86_64 ;; Linux-aarch64) asset=linux-arm64 ;;
Darwin-x86_64) asset=macos-x86_64 ;; Darwin-arm64) asset=macos-arm64 ;;
*) echo "unsupported platform"; exit 1 ;;
esac
curl -fL -o /tmp/dsync "https://github.com/scaleninja/drivesync/releases/latest/download/dsync-$asset" \
&& chmod 755 /tmp/dsync && sudo mv /tmp/dsync /usr/local/bin/dsync
Or from source with a Rust toolchain: git clone https://github.com/scaleninja/drivesync && cd drivesync && make install.
Setup
dsync talks to Drive with an OAuth client that you create, so no credentials are baked into
the binary. One-time steps in the Google Cloud Console
(there is a step-by-step walkthrough with screenshots):

Create a project (or pick one).
APIs & Services → Library: enable the Google Drive API.
APIs & Services → OAuth consent screen: choose External, name the app (this name appears on
the consent page), fill in the support emails, and add yourself under Test users.
APIs & Services → Credentials → Create Credentials → OAuth client ID: type Desktop app,
then Download JSON.

Then initialize a folder. A browser opens for consent; the tool receives the code on a loopback port
and stores its tokens in .gd/ inside the folder (mode 0600, never uploaded).
dsync init ~/gdrive --remote-folder backups/lab --credentials ~/Downloads/client_secret.json

Google expires refresh tokens after 7 days for External apps still in Testing. If you are asked
to re-run init every week, click Publish app on the consent screen (no verification is needed
for personal use).

Usage
Run any command from anywhere inside the sync folder. PATH is relative to the current directory
and may be a file or a folder; it defaults to ..

Command
Does

init [DIR] --remote-folder P --credentials F
Authorize and turn DIR into a sync folder mirroring My Drive/P. --depth N limits levels (-1 = unlimited).

push [PATH]
Upload files that are new or newer locally. Shows the plan and asks first.

pull [PATH]
Download files that are new or newer on Drive. Shows the plan and asks first.

diff [PATH]
List what differs, with both modification times. Changes nothing; exit status 1 if anything differs.

status
Local folder, remote folder, depth, cache state, ignore file, filesystem kind, token expiry.

update-cache
Refresh the index of the remote tree only.

version
Print the version.

Options for push, pull and diff:

Option
Effect

-y, --no-prompt
Apply without asking. Refused if the plan contains conflicts.

--force
Also overwrite conflicts where the destination is newer or has different content at the same mtime. Case collisions are never forced.

-j N, --threads N
Parallel streams (default 8, max 64).

--refresh
Ignore the cached index and re-list the whole remote tree.

--fast
rsync-style quick check: equal size and mtime is trusted without reading the file.

--verify
Re-read every file that needs hashing instead of trusting the local hash cache.

Put gitignore-style patterns in .driveignore at the sync root to leave things out (init creates
one with .DS_Store and ._*). .gd/, symlinks and non-regular files are never synced.
Exit status is 0 on success, 1 when diff found differences, and 2 on an error or when any
transfer failed. PATH is taken in its on-disk spelling, so on macOS push Docs and push docs
mean the same folder. Only one dsync command runs in a workspace at a time; a second one waits.
How it decides what changed
Each side is a set of paths with size, mtime and MD5. Drive computes MD5s server-side and returns
them in listings, so the remote side is free. Files are compared cheapest check first:

Different sizes → different, nothing is read.
Otherwise the local MD5 is compared. It is computed once and cached against the file's size and
mtime, so after the first run only files whose stat changed are read again (--verify bypasses
the cache; --fast skips hashing when size and mtime match).
Equal MD5 → identical, whatever the mtimes say.
Different MD5 → the newer side (1 s tolerance) wins. Same mtime with different content is a
conflict.

After a transfer both sides carry the same mtime, so diff reports nothing until something changes.
The remote tree is indexed once in full, then kept current through the Drive Changes API, so
repeated runs do not re-list Drive.
Push and pull print one line per change and ask before doing anything:
+ photos/2026/
+ photos/2026/a.jpg 1,234,567 B
M docs/report.txt 12,340 B, local newer
! docs/Budget skipped: remote is a Google-native document; not overwritten
C notes.txt conflict: remote is newer
Addition count 2 src: 1,234,567 B
Modification count 1 src: 12,340 B
Proceed with the changes? [Y/n]:

+ create · M overwrite · ! skipped for a structural reason · C conflict · E unreadable local file.
What it will never do

Delete anything, on either side. A file removed locally stays on Drive, and vice versa.
Overwrite a conflict without --force, or a name collision (case or Unicode normalization,
on filesystems that fold them) at all. While conflicts exist the
prompt defaults to no, --no-prompt refuses to run, and end-of-input is never taken as yes.
Write stale data. Every destination is re-checked right before it is written: a local file must
still have the size and mtime the plan saw; a Drive file must still have the MD5, mtime, name and
parent folder the plan saw, a file to be created must still be absent, and every folder new
content goes into must still sit where the index placed it inside the sync tree. Anything that changed
in between is reported and left alone. A local file that changes while it is being read or
uploaded is reported as an error, and every upload is verified against the MD5 Drive computed.
Escape the sync folder. Nothing is written through a symlink, into .gd/, over an ignored
path, or over a non-regular file. Downloads go to a private temp file, are verified against Drive's
MD5, then renamed into place keeping the existing permissions.
Create duplicates. Drive is asked for a same-named entry immediately before every create, and
creates use ids pre-generated by Drive, so neither a retry after a lost response nor a stale index
makes a second copy or adopts someone else's file. Where Drive already holds duplicates, the oldest
one consistently owns the path. Large uploads resume from the last byte Drive received, even across
a restart: the session is kept in .gd/ for as long as the file's size and mtime are unchanged.
Touch Google Docs, Sheets or Slides. They have no binary content and are always skipped.

Files over 5 MB stream through resumable uploads; rate limits, network errors and stalled
connections are retried with backoff; a push or pull interrupted with Ctrl-C can simply be re-run.
If the remote folder is trashed or deleted on Drive, every command stops and says so. A damaged
.gd/cache.db is rebuilt automatically; it is only an index.
Known limitations

Two machines syncing the same Drive folder are not coordinated; there is no three-way merge. The
Drive API has no conditional update, so the check that a remote file is still what the plan saw
and the write that replaces it are two requests; a write from elsewhere in that window is lost.
Pull keeps no local backup: a local file it overwrites (only ever an older one, unless --force)
is gone. Push replaces Drive content, which Drive keeps as a revision for a while.
A full listing holds every My Drive entry in memory while the tree is assembled.
Another local process writing to the sync folder at the same moment as dsync is outside the
supported threat model: destinations are checked immediately before each write, but not atomically
with it.
Keep the sync folder on a local disk. On NFS or SMB shares neither the workspace lock nor the
SQLite index behaves reliably.
The hash cache trusts an unchanged size and mtime, like git; use --verify after restoring files
from a backup or when a tool rewrites files with their mtimes preserved.
Filesystem nuances: on macOS and other case-folding filesystems, names that differ only by case or
Unicode normalization are one local file, so such pairs are reported as collisions and never
transferred (HFS+ stores accented names in NFD form, which can trigger this for names created on
Drive). Filesystems with coarse timestamps (FAT, exFAT, some network shares) cause more hashing
but no wrong decisions, since content is compared by MD5. Names longer than 255 bytes cannot be
local files and are skipped.

Building
make build | release | test | check # check = fmt + clippy
make all-targets # Linux (static musl via cargo-zigbuild) + macOS into dist/
make setup # one-time: rustup targets + cargo-zigbuild (needs zig)

Windows is not supported. CI runs fmt, clippy, tests and a locked build on every push and pull
request; tags v* build all four targets and publish them with a SHA256SUMS file.
To ship builds to your own users with a bundled OAuth client, set both variables at build time;
init then needs no --client-id/--client-secret. Google treats desktop client secrets as
non-confidential, but a bundled client shares one API quota, so keep such builds internal:
DSYNC_CLIENT_ID=... DSYNC_CLIENT_SECRET=... cargo build --release

AboutSuperfast Google Drive CLI to sync files & foldersscaleninja.com/drivesync/Topicsclicloud-storagegoogle-drivesyncResourcesReadmeMIT licenseSecurity policySecurity policyActivityCustom propertiesStars14 starsWatchers0 watchingForks0 forksReport repositoryReleasesPackagesContributorsLanguages

Footer

© 2026 GitHub, Inc.

Footer navigation

Terms

Privacy

Security

Status

Community

Docs

Contact

Manage cookies

Do not share my personal information

You can’t perform that action at this time.

The drivesync tool is a command-line utility written in Rust designed to facilitate synchronization between a local folder and a corresponding folder on Google Drive, modeled after odeke-em/drive. It operates by enabling users to push local changes to the remote Drive, pull remote changes down, or determine the differences between the two states.

The initialization process requires setting up authentication through the Google Cloud Console to establish an OAuth client ID, which allows the tool to interact with the Drive API without baking credentials into the binary. Users initiate the sync by running a command like dsync init where they specify the local directory and the remote folder. This step involves an interactive browser consent flow that grants the necessary permissions for the application to access the user's Drive data, storing tokens securely in a specified location.

The core operations involve specific commands: push uploads newer or new local files, pull downloads newer or new remote files, and diff which lists discrepancies based on modification times. The tool employs sophisticated logic to determine changes by comparing paths along with their size, modification times, and MD5 hashes. It prioritizes efficiency by checking file sizes first; if sizes differ, no hash comparison is necessary. If sizes match, the local MD5 is compared against the remote computation. If the MD5s differ, the modification time (with a one-second tolerance) determines the outcome, with the newer version prevailing. Conflicts arising from simultaneous modifications are handled by prompting the user, as the default behavior strictly avoids unresolvable collisions without explicit force options.

The tool maintains strict data integrity during transfers; it verifies that local files still possess the expected size and modification time before writing to Drive, and conversely, that remote files maintain the expected MD5, modification time, and parent folder structure. It is designed to prevent accidental deletions on either side of the synchronization and ensures that any transfer is verified against the computed MD5 values, mitigating data corruption risks.

The tool incorporates several safeguards concerning file operations. It avoids writing through symbolic links, ignored paths specified in .driveignore, or non-regular files. It manages conflicts by default refusing to overwrite content unless explicitly instructed via options like --force, and it ensures that when creating entries on Drive, unique identifiers are generated to prevent duplication or adopt unwanted files. For large file transfers, the system utilizes resumable uploads with error handling that allows for retries in case of network interruptions.

Known limitations exist concerning multi-machine coordination, where simultaneous synchronization between different local machines is not managed for three-way merging, and a pull operation does not provide local backup of overwritten files. Furthermore, performance considerations involve an in-memory full listing during tree assembly. The system also has specific filesystem nuances; it reports potential collisions for file names that only differ in case or Unicode normalization across systems like macOS, and while it computes hashes for files on filesystems with coarse timestamps such as exFAT, content comparison via MD5 ensures correctness regardless of timestamp precision.

Building the application involves using `make` commands to compile targets, which supports Linux via static musl compilation and macOS using cargo-zigbuild. For deployment, the build process incorporates quality checks and generates checksums for published artifacts, although a configuration exists to bundle OAuth client secrets for building applications intended for distribution.