Repository navigation
Releases: BrianWeiHaoMa/git-dupe
Releases · BrianWeiHaoMa/git-dupe
Release list
v0.3.2
Added
- After a private
merge,rebase, orpullwhose autostash, asked for by
--autostashor anautoStashsetting such asmerge.autoStash, came back with
conflicts, which Git ends with status 0 and keeps as a stash entry,git dupenow
ends with ahint:naminggit dupe add <path>andgit dupe stash dropto keep the
changes, orgit dupe reset --hardand latergit dupe stash popto set them aside,
because Git's own advice there names plaingit stash dropandgit stash pop, and in
recent releasesgit reset --hard, which act on the project's repository. Git's
output and exit status are unchanged, and the manual page says so under PLAIN GIT. - After a private
commit --amendwithout--allow-emptythat exits 1, as one that
would leave the commit empty does,git dupenow ends with ahint:naming
git dupe commit --amend --allow-emptyandgit dupe reset HEAD^, and after a
privatecommitthat exits 1 while acherry-pickis stopped, as one does once the
pick has become empty, with the hint of the stoppedcherry-pick, naming
git dupe commit --allow-emptyandgit dupe cherry-pick --skip, or, where a rebase
stands stopped there too, as at a pick that became empty, with the hint of the stopped
rebase, naminggit dupe rebase --skip, because Git's own advice after the amend
names plaingit reset HEAD^, and after a commit during an empty pick plain
git commit --allow-emptyandgit cherry-pick --skiporgit rebase --skip, which
act on the project's repository. Git's output and exit status are unchanged. - After a private
pushgiven no refspec that Git refuses on a branch whose upstream
has another name than the branch, underpush.defaultunset orsimple,git dupe
now ends with ahint:naminggit dupe push <remote> HEAD:<upstream>and
git dupe push <remote> HEAD, and after one that Git refuses on a detachedHEAD,
with a hint naminggit dupe push <remote> HEAD:<branch>, because Git's own advice
there names plaingit push, which acts on the project's repository. Git's output and
exit status are unchanged. - After a private
pullgiven no refspec that exits 1 on a detachedHEAD,git dupe
now ends with ahint:naminggit dupe pull <remote> <branch>, and after one told
to fast-forward only, by--ff-onlyorpull.ff=only, that Git refuses, as it does
where the branches have diverged, with a hint naming
git dupe merge --no-ff FETCH_HEAD, which merges what the pull fetched where it was
given no--append, andgit dupe rebase FETCH_HEAD, which rebases onto it where the
pull also fetched one branch from one remote alone, because Git's own advice there
names plaingit pull, orgit merge --no-ffandgit rebase, which act on the
project's repository. Git's output and exit status are unchanged.
Fixed
git dupe cleangiven a pathspec that is not a literal path, one holding*,?,
[, or\, or beginning with:other than:/alone, such as:(exclude)notes/x,
:/!notes/x, orcache?, is now refused, deleting nothing, while a hidden path lies in a directory
that is not hidden and under which the project tracks nothing, where Git's clean could
delete that directory whole, the never-committed private files in it included. The
refusal names the pathspec, the directory, and the hidden path; literal pathspecs and
-epatterns run as before, and the manual page explains the refusal under REFUSED
COMMANDS.git dupe detachwithout--forcenow decides whether a private commit is on a
remote-tracking branch over the history as stored, replace refs (git replace) and a
grafts file (info/grafts) set aside, as a remote holds it: a commit that only a
replace ref or a grafts line grafts under pushed history is refused as unpushed, where
it was removed with the private repository, and pushed history that a replace ref or a
grafts line cuts away from its descendants no longer refuses.git dupe push,pull, andfetchare now refused, sending and fetching nothing,
while a member of a private remote group, a word of the value of anyremotes.<group>
key, names the project's repository or a remote of it, whatever the command's own
words name, the refusal naming the key andgit dupe config --unset remotes.<group>:
git dupe fetch <group>andpull <group>with two or more members, and from Git
2.55.0git dupe push <group>, ran against each member unguarded.git dupe pullgiven-jor--jobswith no value attached, which Git hands to
fetchahead of the repository word, so thatfetchtakes that word as its number of
jobs and fetches from the default remote, as forgit dupe pull --jobs --ff 4, is now
refused, fetching nothing, where that default remote names the project's repository
or a remote of it; it ran unguarded wherepullkept a word of its own between the
two. So is agit dupe pullgiven a word beginning with-after--, as
git dupe pull -- -q, whichpullhands tofetchwithout the--, so thatfetch
reads it as an option of its own and takes the default remote where no later word
names one: it is refused where that default remote names the project's repository or
a remote of it, whatever the later words name; it ran unguarded.- On a branch with no upstream, a
git dupe push <remote>with no refspec that Git
refuses, where<remote>is the branch'sbranch.<name>.remote, or, where that is
unset, the only remote the private configuration defines,<remote>counted among
them, andoriginwhere it defines several, now ends with thehint:naming
git dupe push -u <remote> HEADthatgit dupe pushalready gets, and a failed
git dupe pull <remote>with no refspec, where<remote>is the branch's
branch.<name>.remote, with the hint naminggit dupe pull <remote> <branch>and
git dupe branch --set-upstream-to, because Git's own advice there names plain
git push --set-upstreamorgit pull, which act on the project's repository. On a
branch with no commit yet, the hint naminggit dupe push -u <remote> HEADnow names
git dupe commitfirst, because that push has nothing to push before it, and it now
also follows on a branch withbranch.<name>.mergeset and no
branch.<name>.remote, which Git's push counts as having no upstream. The hint after
agit dupe pullon a branch with no upstream now follows there too, which Git's pull
counts so as well, and only where Git exits 1: given a URL or a path as the branch's
remote, Git has the remote'sHEADto merge and, where it cannot, exits 128, and the
hint of a pull that cannot reconcile divergent branches follows instead. Git's output
and exit status are unchanged. - A private
rebase -ithat stops at aneditor abreak, which Git ends with status
0, now ends with thehint:a stopped rebase gets, and that hint now also names
git dupe commit --amendandgit dupe rebase --edit-todo, because Git's own advice
at aneditstop, a reword whose editor failed, or a todo list Git cannot read names
plaingit commit --amendorgit rebase --edit-todo, which act on the project's
repository. The hint of a stoppedrebaseoramnow also follows any run of that
command that leaves its stop standing, whatever its exit, as
git dupe rebase --show-current-patchorgit dupe am --show-current-patchdoes with
status 0. A privateamrun while a rebase is stopped, and arebaserun while an
amis stopped, no longer end with the hint of the other, whose commands fail there,
and acherry-pickorrevertrun while a rebase is stopped no longer ends with the
hint of a stoppedcherry-pickorrevert, whose commands leave a rebase stopped at
a pick that became empty as it was. Git's output and exit status are unchanged. - A
git dupe revertgiven-n, alone or in a bundle of short options such as-sn,
or--no-commit, that stops on a conflict no longer ends with thehint:naming
git dupe revert --continue, which commits what--no-commitwas given to leave
uncommitted; Git's own advice there names onlygit addandgit rm. The hint after
a stoppedrevertnow also namesgit dupe commit --allow-empty, as the one after a
stoppedcherry-pickdoes. - The
hint:after a rejectedgit dupe pushgiven a repository now names that
repository, asgit dupe pull <remote> <branch>andgit dupe fetch <remote>, where
it named the plaingit dupe pullandgit dupe fetch, which take the branch's
upstream. A word holding=, as--recurse-submodules=check, and a negated option,
as--no-rebase, are no longer read as taking the next word as their value, so that
git dupe push --recurse-submodules=check origin mainand
git dupe pull --no-rebase origin mainare read as Git reads them, given the
repositoryorigin. - The
hint:after agit dupe checkoutorswitchthat leaves a branch for a detached
HEADnow says thatgit dupe switch -goes back to the branch left only where that
branch has a commit: from a branch with none yet, it has nothing to go back to. - The
hint:naminggit dupe branch -Dafter agit dupe branch -dthat refuses now
also follows an abbreviation of--deletethat Git reads as it, as--delbefore Git
2.56.0, whosebranchalso has--delete-mergedand refuses the abbreviation as
ambiguous, exit 129, hinting nothing, anddin a bundle of short options, as-vd,
where Git's own advice names plaingit branch -D, which acts on the project's
repository.
v0.3.1
Added
- After a private
rebaseoramthat stopped, acherry-pickorrevertthat
stopped, acheckoutorswitchthat left a branch for a detachedHEAD, a
branch -dthat refused, apushfrom a branch with no upstream, a rejectedpush,
apullon a branch with no upstream, apullthat finds divergent branches with no
way to reconcile them configured, and anaddof a path the private repository
ignores,git dupenow ends with ahint:naming thegit dupecommands to go on
with, such asgit dupe rebase --continue,git dupe push -u origin HEAD, or
git dupe add -f <path>, because Git's own advice there names plaingitcommands,
which act on the project's repository. Git's output and exit status are unchanged,
and the hint also follows such a command reached through an alias. The manual page
says so under PLAIN GIT.
Fixed
git dupe hideof a path an ignore rule ignores, such as.env.localunder the
project's.gitignore, now also offersgit dupe add -ffor it, where its hint
offered only the plaingit dupe add, which Git refuses for an ignored path; for a
path beyond a symbolic link, which Git's add does not follow, the hint names the link
and offers nogit dupe add.git dupe status, while nothing but.gitdupeis hidden, says so in its hint, where
it said that nothing was private yet even after private files had been committed and
then released.git dupe clean, refusing while a hidden path lies beyond a symbolic link, says that
Git's clean does not follow the link and can delete it, the hidden path's only way to
what stands at it, where it said that Git would delete the link and what it leads to.git dupe detachwithout--forcenow refuses while the commitHEADnames, such as
one made on a detachedHEAD, is on no remote-tracking branch, as it already refused
for a commit on a branch, tag, or stash entry, where it removed the private repository
and that commit with it.git dupe initin an attached workspace whose private repository has a sparse
checkout (git dupe sparse-checkout set) now reads and records the work tree in
config.worktree, where the sparse checkout moved it and where Git reads it first:
aftergit worktree moveit records the linked worktree's new place there, so that
plain Git reads the private repository again, where it wrote the local file that
config.worktreeoutranks; and on an attachment that was already complete it changes
nothing, where it took the attachment for unfinished and set the identity,
status.showUntrackedFiles, andadvice.statusHintsagain over the developer's own
values.git dupe statusnow shows the untracked and ignored files under a directory that
stands, below a hidden path, where the project's Git tracks a file, as when a draft
directory replaces a teammate's file, where it left out the directory and everything
in it; the project's file itself is still not listed, and a directory holding a
.gitentry that stands at such a path, as a submodule's checkout does, is not
listed as untracked unless the deletion of a private file at that same path is
staged.git dupe push,pull,fetch,remote, andclonenow refuse a URL of the
project's remote whose credentials hold an encoded/, as
https://reader:p%2Fss@example.com/team/project.gitdoes, and every other spelling
of the project's remote when its own configured URL is spelled so, where they read
the host from the credentials, before the%2F, and let the URL through. Afile://
URL so spelled is compared by the path after its host as well as by the path Git
opens, which the encoded/moves.git dupe push,pull,fetch,remote, andclonenow refuse afile://URL of
the project's repository whose path holds%00, which Git keeps as written and
opens, where they read it as a zero byte and let the URL through.
v0.3.0
Added
- macOS support, on Apple silicon and on Intel, the default case-insensitive volume
included. A release has an archive for each kind of Mac,
git-dupe-<version>-macos-arm64.tarfor macOS 11.0 or newer on Apple silicon and
git-dupe-<version>-macos-x86_64.tarfor macOS 10.12 or newer on Intel, each built
and checked on a Mac of its kind, with the same manual page and install note as the
Linux archive. On a case-insensitive volume git-dupe compares paths as spelled, so a
path spelled in another case is another path to it, while the project's Git ignores
a hidden path in every spelling of its ASCII case.
Changed
- The release archive is named for the platform and the architecture it is built for,
git-dupe-<version>-linux-x86_64.tarfor x86-64 Linux, where its name held the
version alone; what it holds and its install note are unchanged. - The Linux executable is built on Ubuntu 24.04 and needs the GNU C library 2.39 or
newer, where it needed 2.34; on an older system, install from source.
Fixed
git dupe statusshows Git's status when thousands of private files below a hidden
directory have their deletion staged, where it was refused once their paths together
no longer fit on one command line; an untracked directory standing where they were is
shown as one entry, as Git shows one.git dupe detachremoves the private repository when a hidden directory holds
thousands of private files whose paths together do not fit on one command line,
where it was refused,--forceincluded, after removing the region, so that the
project's Git saw the private files until the nextgit dupecommand.git dupe cloneof a shallow private repository, such as one made shallow by
git dupe fetch --depth=1, checks out its branch and writes the private files, the
new private repository shallow asgit cloneof it would be, where it checked out
nothing and said that the remote had no branch.
v0.2.0
Added
- Linked worktrees as workspaces of their own:
git dupe initandgit dupe clone
attach a worktree thatgit worktree addmade, of an ordinary or a bare repository,
with its own private repository in its Git directory (.git/worktrees/<name>/dupe),
its own hidden paths and history, and its own region of.git/info/exclude, where
git dupein a linked worktree was refused before. Private work moves between
worktrees as between machines, andgit dupe initrun aftergit worktree move
records the new place for plain Git. Commands in different worktrees may run at the
same time: each waits for another's write of.git/info/excludeto finish, so that
no worktree's region update is lost, and one killed while it writes the file holds
up none. Every command that keeps its region up to date warns about a path that
another worktree hides, while it stands in this one, is not hidden here, and the
project's Git ignores it, naming that worktree, andgit dupe detachnames each
formerly hidden path another worktree still hides. The region of a worktree whose
private repository is gone, aftergit worktree removeorprune, is dropped by
the next such command, ordetach, in any worktree.
Changed
- The first line of
git dupe helpand the manual page's quick start and examples
present git-dupe as versioning the files of a checkout that are yours and not the
project's, notes and an instruction file for a coding agent among them: the quick
start runsgit dupe add notes/ AGENTS.md, and the-fthat a file the project
ignores takes is shown in the examples, with.vscode/, and explained under FILES
THE PROJECT IGNORES. No command changes.
Fixed
- Where a symbolic link or a file stands at
.git/dupe, or at a linked worktree's
dupe, git-dupe no longer runs Git through it:git dupe init,git dupe clone,
and a help request such asgit dupe commit -m -hare refused naming it, where they
could write into whatever repository it led to, the project's own included. - A command that a line of git-dupe offers to run, such as
git dupe restoreafter
clonekept a file, orgit rm --cachedbefore a file becomes private, now acts on
the path it names alone when typed as shown. A path holding a space, a quote,$, or
!is quoted for the shell, where it was split or expanded; one beginning with:, or
holding*,?,[, or\, is written so that neither Git nor git-dupe reads it as
a pattern or pathspec magic, wheregit dupe restorecould overwrite other files, and
a Git command offered for such a path works withGIT_LITERAL_PATHSPECSexported.
The alias name in thegit dupe gitcommand offered for an alias Git releases read
differently is quoted the same way.
v0.1.0
Added
- git-dupe, invoked as
git dupe: a developer's private files of a project versioned
in a second Git repository at.git/dupe, and kept out of the project's Git through
rules it maintains in.git/info/exclude. - Its own commands
init,clone,detach,hide,unhide,git, andhelp;
statusandaddacting on the hidden paths, andcleancleaning the project while
sparing them;stashrefusing the forms that would stash the whole project, and
push,pull,fetch,remote, andclonerefusing the project's own repository.
Every other Git command runs against the private repository unchanged. - Support for Git 2.43.0 or newer, below 3.0.0, checked under every minor release from
2.43.0 to 2.56.0. - The manual page
git-dupe(1): the commands, a quick start, examples, and
explanations. - The release archive
git-dupe-<version>.tar: the executable, for x86-64 Linux with
the GNU C library 2.34 or newer, the manual page, and an install note.