<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>~iany/ Git</title><link>https://blog.iany.me/tags/git/</link><description>Recent content in Git «~iany/»</description><language>en-US</language><managingEditor>me@iany.me (Ian Yang)</managingEditor><webMaster>me@iany.me (Ian Yang)</webMaster><copyright>CC-BY-SA 4.0</copyright><lastBuildDate>Fri, 12 Dec 2025 03:22:58 +0800</lastBuildDate><atom:link href="https://blog.iany.me/tags/git/index.xml" rel="self" type="application/rss+xml"/><item><title>Backup Ignored Files with Git Remote Branch</title><link>https://blog.iany.me/2025/12/backup-ignored-files-with-git-remote-branch/</link><pubDate>Fri, 12 Dec 2025 03:22:58 +0800</pubDate><author>me@iany.me (Ian Yang)</author><guid>https://blog.iany.me/2025/12/backup-ignored-files-with-git-remote-branch/</guid><description>&lt;p&gt;When working with Git repositories, there are often files that need to be backed up but shouldn&amp;rsquo;t be committed to the main branch. These might include local development settings, IDE configuration files, personal notes, or development scripts that are specific to your workflow. The challenge is finding a way to back up these ignored files without polluting the main repository history.&lt;/p&gt;
&lt;h2 id="the-original-solution"&gt;The Original Solution&lt;/h2&gt;
&lt;p&gt;My original approach was to use a separate repository to store backup files for all repositories and create symbolic links. This worked, but had several drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Backup files were disconnected from their source repositories, making it harder to track what belongs where&lt;/li&gt;
&lt;li&gt;Backing up new files required moving them to the backup repository and then creating symbolic links&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="a-better-approach-remote-branch"&gt;A Better Approach: Remote Branch&lt;/h2&gt;
&lt;p&gt;Instead of using a separate repository, we can use a remote branch within the same repository. This approach offers several advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Everything stays in one repository&lt;/li&gt;
&lt;li&gt;When you clone the repository, the backup branch comes with it&lt;/li&gt;
&lt;li&gt;Backup files are stored in a separate branch, keeping the main branch clean&lt;/li&gt;
&lt;li&gt;Full Git history for backup files, just like any other branch&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-script"&gt;The Script&lt;/h2&gt;
&lt;p&gt;With AI assistance, I developed &lt;code&gt;git-store-file&lt;/code&gt;, a tool for managing ignored files by storing them in a remote branch (default: &lt;code&gt;origin/_store&lt;/code&gt;). This keeps backup files separate from your working branch without interfering with regular development. The script is available in &lt;a href="https://github.com/doitian/dotfiles-public/blob/master/default/bin/git-store-file"&gt;Python&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="installation"&gt;Installation&lt;/h3&gt;
&lt;p&gt;Download the script and make it executable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;curl -o ~/bin/git-store-file https://raw.githubusercontent.com/doitian/dotfiles-public/master/default/bin/git-store-file
chmod +x ~/bin/git-store-file
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="usage"&gt;Usage&lt;/h3&gt;
&lt;p&gt;The script has four main commands: &lt;code&gt;store&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;restore&lt;/code&gt;, and &lt;code&gt;ls&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="store-files"&gt;Store Files&lt;/h4&gt;
&lt;p&gt;Store one or more files to the remote branch, including files that are ignored by Git:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# Store a single file
git-store-file config.local.json
# Store multiple files
git-store-file config.local.json secrets.env
# Use a custom branch name
git-store-file --branch backup config.local.json
# Use a custom remote
git-store-file --remote upstream config.local.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you run the store command, the script will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a temporary Git index to avoid conflicts with your working directory&lt;/li&gt;
&lt;li&gt;Load the target branch&amp;rsquo;s current state into the temporary index&lt;/li&gt;
&lt;li&gt;Add the specified files using &lt;code&gt;git add -f&lt;/code&gt; to include ignored files&lt;/li&gt;
&lt;li&gt;Create a commit with the changes&lt;/li&gt;
&lt;li&gt;Display commit statistics and prompt for confirmation&lt;/li&gt;
&lt;li&gt;Push the commit to the remote branch&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="check-status"&gt;Check Status&lt;/h4&gt;
&lt;p&gt;Check which files stored in the remote branch have been modified in your local working directory:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# Check status
git-store-file status
# Show diff for modified files
git-store-file status --diff
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="restore-files"&gt;Restore Files&lt;/h4&gt;
&lt;p&gt;Restore files from the remote branch to your working directory:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# Restore a specific file
git-store-file restore config.local.json
# Restore all files from the branch
git-store-file restore
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="list-files"&gt;List Files&lt;/h4&gt;
&lt;p&gt;List all files stored in the remote branch:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;git-store-file ls
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="options"&gt;Options&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-b, --branch BRANCH&lt;/code&gt;: Specify the branch name (default: &lt;code&gt;_store&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-r, --remote REMOTE&lt;/code&gt;: Specify the remote name (default: &lt;code&gt;origin&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-h, --help&lt;/code&gt;: Show help message&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="examples"&gt;Examples&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# Store local configuration
git-store-file .env.local
# Check what's changed
git-store-file status -d
# Restore after cloning
git-store-file restore .env.local
# List all backed up files
git-store-file ls
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="how-it-works"&gt;How It Works&lt;/h2&gt;
&lt;p&gt;The script uses Git&amp;rsquo;s low-level plumbing commands to manipulate the repository without affecting your working directory:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Temporary Index&lt;/strong&gt;: Uses the &lt;code&gt;GIT_INDEX_FILE&lt;/code&gt; environment variable to create a temporary index, completely isolated from your working directory&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Read Tree&lt;/strong&gt;: Loads the target branch&amp;rsquo;s tree structure into the temporary index&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Force Add&lt;/strong&gt;: Uses &lt;code&gt;git add -f&lt;/code&gt; to add files even if they&amp;rsquo;re listed in &lt;code&gt;.gitignore&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commit Tree&lt;/strong&gt;: Creates a commit object directly using &lt;code&gt;git commit-tree&lt;/code&gt;, bypassing the normal commit workflow&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Direct Push&lt;/strong&gt;: Pushes the commit hash directly to the remote branch reference&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This low-level approach ensures that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your working directory remains unchanged&lt;/li&gt;
&lt;li&gt;The main branch is never affected&lt;/li&gt;
&lt;li&gt;Ignored files can be stored without modifying &lt;code&gt;.gitignore&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The backup branch maintains full Git history&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;git-store-file&lt;/code&gt; script solves the problem of backing up ignored files in a clean, Git-native way. By leveraging a remote branch, it keeps everything in one repository while maintaining clear separation between your main codebase and backup files. Whether you&amp;rsquo;re managing local configurations, IDE settings, or personal development scripts, this tool provides a simple and reliable backup solution that integrates seamlessly with your existing Git workflow.&lt;/p&gt;</description><category domain="https://blog.iany.me/post/">Posts</category><category domain="https://blog.iany.me/tags/git/">Git</category><category domain="https://blog.iany.me/tags/automation/">Automation</category><category domain="https://blog.iany.me/tags/backup/">Backup</category></item><item><title>Pass, A Password Manager Utilizing GPG and Git</title><link>https://blog.iany.me/2020/05/pass-a-password-manager-utilizing-gpg-and-git/</link><pubDate>Sun, 03 May 2020 22:50:53 +0800</pubDate><author>me@iany.me (Ian Yang)</author><guid>https://blog.iany.me/2020/05/pass-a-password-manager-utilizing-gpg-and-git/</guid><description>&lt;p&gt;A friend recommended &lt;a href="https://github.com/gopasspw/gopass"&gt;gopass&lt;/a&gt; to manage passwords. After the trial, I decided to switch.&lt;/p&gt;
&lt;p&gt;Gopass is indeed an implementation which follows the protocols defined by &lt;a href="https://www.passwordstore.org/"&gt;pass&lt;/a&gt;. It utilizes gpg to encrypt files and git to synchronize. It is really fast to manage the password vault because I already used to the command line environment and text editing tools.&lt;/p&gt;
&lt;p&gt;I also use &lt;a href="https://github.com/browserpass/browserpass-extension"&gt;browserpass&lt;/a&gt; in Chrome and &amp;ldquo;Pass - Password Manager&amp;rdquo; in iOS.&lt;/p&gt;
&lt;p&gt;I migrated from Enpass. There is a tool &lt;a href="https://github.com/roddhjav/pass-import"&gt;pass-import&lt;/a&gt;, but it does not work well on my exported Enpass vault, so I decide to build the pass store from scratch. Gopass indeed can encrypt and save any file, thus I just add the whole exported Enpass CSV file into the repository.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gopass edit enpass.csv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy and paste the exported Enpass CSV file into the opened editor and save. It is easy to search in the file using a pager or grep.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gopass show enpass.csv | less
gopass show enpass.csv | grep google
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I can rebuild the password database with a create-on-use strategy: Whenever I want to use a password which has no its own entry yet, I search it in &lt;code&gt;enpass.csv&lt;/code&gt; and create one.&lt;/p&gt;</description><category domain="https://blog.iany.me/post/">Posts</category><category domain="https://blog.iany.me/tags/git/">Git</category><category domain="https://blog.iany.me/tags/gpg/">GPG</category><category domain="https://blog.iany.me/tags/password-manager/">Password Manager</category><category domain="https://blog.iany.me/tags/security/">Security</category></item></channel></rss>