git-ssh-sync 0.4.0__tar.gz → 0.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: git-ssh-sync
3
- Version: 0.4.0
3
+ Version: 0.5.1
4
4
  Summary: Sync Git commits through a local machine for development environments without direct GitHub or GitLab access.
5
5
  Requires-Dist: pydantic>=2.13.4
6
6
  Requires-Dist: pyyaml>=6.0.3
@@ -23,8 +23,74 @@ Description-Content-Type: text/markdown
23
23
 
24
24
  This tool is designed for niche environments where outbound network access is restricted, such as high-security enterprises and projects that only allow limited inbound communication (e.g., SSH, RDP).
25
25
 
26
+ ## Start here
27
+
28
+ If you are new to `git-ssh-sync`, read these sections in order:
29
+
30
+ | Goal | Section |
31
+ |---|---|
32
+ | Check whether this tool fits your environment | [Who is this for?](#who-is-this-for) |
33
+ | Understand the repository layout | [Architecture](#architecture) |
34
+ | Install and run the shortest setup | [Quick start](#quick-start) |
35
+ | Register a real project | [Configuration](#configuration) |
36
+ | Work day to day | [Daily Development Workflow](#daily-development-workflow) |
37
+ | Recover from stopped synchronization | [Troubleshooting](#troubleshooting) |
38
+
39
+ The common path is:
40
+
41
+ 1. Install `git-ssh-sync` on the local machine.
42
+ 2. Register a project with `init`.
43
+ 3. Create or attach the development repositories with `clone` or `attach`.
44
+ 4. Run `pull` before editing and `push` after committing on the development environment.
45
+
46
+ ## Who is this for?
47
+
48
+ Use `git-ssh-sync` if:
49
+
50
+ - Your development environment cannot access GitHub / GitLab directly.
51
+ - Your local machine can access GitHub / GitLab.
52
+ - Your local machine can SSH into the development environment.
53
+ - You want to edit, build, test, and commit in the development environment.
54
+ - You want to synchronize by Git commits and branches instead of copying files manually.
55
+
56
+ If your development environment can already access GitHub / GitLab directly, you usually do not need this tool.
57
+
26
58
  This is not a file synchronization tool. It synchronizes Git objects and branches. Source editing, building, testing, and committing are performed in the development environment, while communication with GitHub/GitLab is handled by the local machine.
27
59
 
60
+ ## Architecture
61
+
62
+ `git-ssh-sync` keeps GitHub/GitLab access on the local machine and Git work on
63
+ the development environment.
64
+
65
+ ```text
66
+ origin: GitHub / GitLab
67
+ ↑↓
68
+ local gateway repo
69
+ ↑↓ git over SSH
70
+ dev bare cache repo
71
+ ↑↓
72
+ dev work repo
73
+ ```
74
+
75
+ Terms used throughout this document:
76
+
77
+ | Term | Meaning |
78
+ |---|---|
79
+ | `origin` | Original remote repository on GitHub / GitLab |
80
+ | `local gateway repo` | Relay repository on the local machine |
81
+ | `dev bare cache repo` | Bare repository on the development environment |
82
+ | `dev work repo` | Repository where you edit, build, test, and commit on the development environment |
83
+ | `gitsync remote` | Remote in the dev work repo that points to the dev bare cache repo |
84
+
85
+ ## Current limitations
86
+
87
+ The following features are not supported yet:
88
+
89
+ - Git LFS
90
+ - Git submodules
91
+ - automatic conflict resolution
92
+ - synchronizing uncommitted file changes
93
+
28
94
  ## Prerequisites
29
95
 
30
96
  `git-ssh-sync` assumes the following configuration:
@@ -37,20 +103,25 @@ Local machine
37
103
  Development environment
38
104
  ```
39
105
 
40
- Local machine:
106
+ | Place | Requirements |
107
+ |---|---|
108
+ | Local machine | Can access GitHub / GitLab, can SSH to the development environment, and has `git` and `uv` available |
109
+ | Development environment | Can be accessed via SSH from the local machine, has `git` available, and does not need direct GitHub / GitLab access |
41
110
 
42
- - Can access GitHub / GitLab
43
- - Can SSH to the development environment
44
- - Has `git` and `uv` available
45
- - Uses `git-ssh-sync` for commit synchronization, status checks, and diagnostics between GitHub/GitLab and the development environment
111
+ Run `git-ssh-sync` on the local machine. Edit, build, test, and commit on the
112
+ development environment. Synchronization between the two sides happens through
113
+ Git commits and branches.
46
114
 
47
- Development environment:
115
+ ## Safety model
48
116
 
49
- - Can be accessed via SSH from the local machine
50
- - Cannot directly access GitHub / GitLab from the development environment
51
- - Has `git` available
52
- - Performs source editing, building, testing, and committing
53
- - Synchronizes with GitHub/GitLab via the local machine
117
+ `git-ssh-sync` does not:
118
+
119
+ - Synchronize uncommitted files
120
+ - Automatically merge or rebase branches
121
+ - Force-push to origin
122
+ - Modify a dirty development work tree
123
+ - Require GitHub/GitLab credentials on the development environment
124
+ - Require direct outbound access from the development environment to GitHub/GitLab
54
125
 
55
126
  ## Installation
56
127
 
@@ -72,6 +143,37 @@ After installation, verify that the command can be executed.
72
143
  git-ssh-sync --help
73
144
  ```
74
145
 
146
+ ## Quick start
147
+
148
+ After installing `git-ssh-sync`, the shortest path from setup to daily sync is:
149
+
150
+ ```bash
151
+ uv tool install git-ssh-sync
152
+
153
+ git-ssh-sync init myproject \
154
+ --origin git@github.com:example/myproject.git \
155
+ --dev-host devserver \
156
+ --dev-user user \
157
+ --dev-path /home/user/work/myproject
158
+
159
+ git-ssh-sync clone myproject
160
+ git-ssh-sync doctor myproject
161
+
162
+ git-ssh-sync pull myproject
163
+
164
+ # On the development environment:
165
+ # cd ~/work/myproject
166
+ # git add .
167
+ # git commit -m "Add feature"
168
+
169
+ git-ssh-sync status myproject
170
+ git-ssh-sync push myproject
171
+ ```
172
+
173
+ Run `clone` and `doctor` for the first setup. For regular work, run `pull`
174
+ before editing, commit on the development environment, then check `status` and
175
+ run `push` from the local machine.
176
+
75
177
  ## Configuration
76
178
 
77
179
  First, register the project you want to synchronize.
@@ -229,11 +331,8 @@ git-ssh-sync clone myproject
229
331
  git-ssh-sync doctor myproject
230
332
  ```
231
333
 
232
- `clone` creates a gateway repository on your local machine and deploys cache and work repositories on the development environment.
233
-
234
- - Gateway repository: Relay repository on the local machine
235
- - Cache repository: Bare repository on the development environment
236
- - Work repository: Repository where actual editing, building, testing, and committing are performed on the development environment
334
+ `clone` creates the local gateway repo and deploys the dev bare cache repo and
335
+ dev work repo described above.
237
336
 
238
337
  Afterward, the work repository on the development environment can be used as a normal Git repository.
239
338
 
@@ -264,6 +363,16 @@ execution after reviewing the plan.
264
363
  git-ssh-sync attach myproject --yes
265
364
  ```
266
365
 
366
+ Use this table to choose between setup diagnostics, wiring repair, and recovery
367
+ after an interrupted sync.
368
+
369
+ | Situation | Command |
370
+ |---|---|
371
+ | Check initial setup or connectivity | `git-ssh-sync doctor myproject` |
372
+ | Repair missing or mismatched `gitsync` remote/cache wiring | `git-ssh-sync doctor myproject --repair` |
373
+ | Diagnose after interrupted `pull` / `push` | `git-ssh-sync recover myproject` |
374
+ | Apply only safe wiring repairs after interruption | `git-ssh-sync recover myproject --yes` |
375
+
267
376
  If only the `gitsync` remote or cache wiring is missing or mismatched, run
268
377
  `doctor --repair` to inspect and repair it through the same preflight checks.
269
378
 
@@ -477,58 +586,205 @@ When diverged, automatic resolution is not performed. Follow "Workflow When Push
477
586
 
478
587
  ## Common Commands
479
588
 
480
- ```bash
481
- # Display help
482
- git-ssh-sync --help
589
+ | Goal | Command |
590
+ |---|---|
591
+ | Display help | `git-ssh-sync --help` |
592
+ | Register a project | `git-ssh-sync init myproject --origin git@github.com:example/myproject.git --dev-host devserver --dev-user user --dev-path /home/user/work/myproject` |
593
+ | List registered project settings | `git-ssh-sync config list` |
594
+ | Show registered project settings | `git-ssh-sync config show myproject` |
595
+ | Initial clone | `git-ssh-sync clone myproject` |
596
+ | Check synchronization status | `git-ssh-sync status myproject` |
597
+ | Check branch status | `git-ssh-sync branch myproject` |
598
+ | Inspect development work repo status | `git-ssh-sync dev status myproject` |
599
+ | Inspect development work repo diff | `git-ssh-sync dev diff myproject --stat` |
600
+ | Reflect changes from origin to development environment | `git-ssh-sync pull myproject` |
601
+ | Reflect commits from development environment to origin | `git-ssh-sync push myproject` |
602
+ | Switch development environment branch | `git-ssh-sync checkout myproject feature/foo` |
603
+ | Create and switch to a new branch from a base branch | `git-ssh-sync checkout myproject -b feature/foo --base develop` |
604
+ | Diagnostics | `git-ssh-sync doctor myproject` |
605
+ | Diagnose after an interrupted sync | `git-ssh-sync recover myproject` |
606
+ | Apply safe recovery repairs | `git-ssh-sync recover myproject --yes` |
483
607
 
484
- # Register a project
485
- git-ssh-sync init myproject \
486
- --origin git@github.com:example/myproject.git \
487
- --dev-host devserver \
488
- --dev-user user \
489
- --dev-path /home/user/work/myproject
608
+ For commands with many options, prefer the full examples in the workflow
609
+ sections above. They are easier to copy safely because each option is shown on
610
+ its own line.
490
611
 
491
- # List registered project settings
492
- git-ssh-sync config list
612
+ ## Troubleshooting
493
613
 
494
- # Show registered project settings
495
- git-ssh-sync config show myproject
614
+ Use `status` first when synchronization stops or the current state is unclear.
615
+ Use `doctor` for setup, connectivity, and repository wiring problems. Use
616
+ `recover` after an interrupted `pull` or `push`.
496
617
 
497
- # Initial clone
498
- git-ssh-sync clone myproject
618
+ ### push stops because origin has new commits
499
619
 
500
- # Check synchronization status
620
+ Cause:
621
+ origin has commits that are not included in the development environment branch,
622
+ or origin and the development environment branch have diverged.
623
+
624
+ Check:
625
+
626
+ ```bash
501
627
  git-ssh-sync status myproject
628
+ ```
502
629
 
503
- # Check branch status
504
- git-ssh-sync branch myproject
630
+ Fix:
631
+
632
+ ```bash
633
+ git-ssh-sync pull myproject
634
+ # If pull cannot fast-forward, merge or rebase in the development environment.
635
+ # See "Workflow When Push Stops" for the detailed recovery flow.
636
+ ```
637
+
638
+ ### pull cannot fast-forward
505
639
 
506
- # Inspect development work repo status
640
+ Cause:
641
+ origin and the development environment branch have diverged. `git-ssh-sync`
642
+ does not perform automatic merge or automatic rebase.
643
+
644
+ Check:
645
+
646
+ ```bash
647
+ git-ssh-sync status myproject
507
648
  git-ssh-sync dev status myproject
649
+ ```
508
650
 
509
- # Inspect development work repo diff
510
- git-ssh-sync dev diff myproject --stat
651
+ Fix:
511
652
 
512
- # Reflect changes from origin to development environment
513
- git-ssh-sync pull myproject
653
+ ```bash
654
+ # On the development environment
655
+ cd ~/work/myproject
656
+ git fetch gitsync
657
+ git merge gitsync/main
658
+ # or: git rebase gitsync/main
659
+ ```
514
660
 
515
- # Reflect commits from development environment to origin
661
+ After resolving conflicts and committing or continuing the rebase, run:
662
+
663
+ ```bash
664
+ git-ssh-sync status myproject
516
665
  git-ssh-sync push myproject
666
+ ```
517
667
 
518
- # Switch development environment branch
519
- git-ssh-sync checkout myproject feature/foo
668
+ ### Development work repo is dirty
520
669
 
521
- # Create and switch to new branch from base branch
522
- git-ssh-sync checkout myproject -b feature/foo --base develop
670
+ Cause:
671
+ the development environment work repo has uncommitted changes. Uncommitted
672
+ changes are not synchronized, and repair commands do not commit, stash, merge,
673
+ or rebase them automatically.
674
+
675
+ Check:
523
676
 
524
- # Diagnostics
677
+ ```bash
678
+ git-ssh-sync dev status myproject
679
+ git-ssh-sync dev diff myproject --stat
680
+ ```
681
+
682
+ Fix:
683
+
684
+ ```bash
685
+ # On the development environment
686
+ cd ~/work/myproject
687
+ git status
688
+ git add <files-to-sync>
689
+ git commit
690
+ ```
691
+
692
+ Commit changes that should be synchronized, or stash/remove local-only changes
693
+ before running `pull`, `push`, `attach`, or `doctor --repair` again.
694
+
695
+ ### gitsync remote is missing or mismatched
696
+
697
+ Cause:
698
+ the `gitsync` remote in the development work repo does not point to the expected
699
+ bare cache repo, or the wiring is missing.
700
+
701
+ Check:
702
+
703
+ ```bash
525
704
  git-ssh-sync doctor myproject
705
+ ```
526
706
 
527
- # Diagnose and optionally repair after an interrupted sync
528
- git-ssh-sync recover myproject
529
- git-ssh-sync recover myproject --yes
707
+ Fix:
708
+
709
+ ```bash
710
+ git-ssh-sync doctor myproject --repair
711
+ git-ssh-sync doctor myproject --repair --yes
712
+ ```
713
+
714
+ ### Cache repo or work repo already exists
715
+
716
+ Cause:
717
+ `clone` was asked to create a development work repo or bare cache repo at a path
718
+ that already exists.
719
+
720
+ Check:
721
+
722
+ ```bash
723
+ git-ssh-sync doctor myproject
724
+ ```
725
+
726
+ Fix:
727
+
728
+ ```bash
729
+ git-ssh-sync attach myproject --dev-path /home/user/work/myproject
730
+ git-ssh-sync doctor myproject --repair
731
+ ```
732
+
733
+ Use `attach` when the existing repositories are intentional. Otherwise choose an
734
+ empty path or move the existing directory before running `clone` again.
735
+
736
+ ### Windows path is broken
737
+
738
+ Cause:
739
+ the local shell may consume backslashes in Windows paths before
740
+ `git-ssh-sync` receives them, or the project may be configured with the wrong
741
+ development OS.
742
+
743
+ Check:
744
+
745
+ ```bash
746
+ git-ssh-sync config show myproject
747
+ git-ssh-sync doctor myproject
530
748
  ```
531
749
 
750
+ Fix:
751
+
752
+ ```bash
753
+ git-ssh-sync init myproject \
754
+ --origin git@github.com:example/myproject.git \
755
+ --dev-host devserver \
756
+ --dev-user user \
757
+ --dev-os windows \
758
+ --dev-path 'C:\Users\user\work\myproject'
759
+ ```
760
+
761
+ Quote Windows paths that contain backslashes when running commands from macOS or
762
+ Linux shells.
763
+
764
+ ### SSH connection fails
765
+
766
+ Cause:
767
+ the local machine cannot connect to the development environment over SSH, or the
768
+ configured host, user, port, or authentication settings are incorrect.
769
+
770
+ Check:
771
+
772
+ ```bash
773
+ git-ssh-sync doctor myproject
774
+ ssh user@devserver
775
+ ```
776
+
777
+ Fix:
778
+
779
+ ```bash
780
+ git-ssh-sync config show myproject
781
+ # Update the project config or recreate it with the correct --dev-host,
782
+ # --dev-user, --dev-port, and SSH authentication settings.
783
+ ```
784
+
785
+ Run `doctor --debug` or use `--log-file` when you need the exact SSH and Git
786
+ commands used during diagnosis.
787
+
532
788
  ## Logging
533
789
 
534
790
  `git-ssh-sync` supports detailed logging for troubleshooting and monitoring synchronization operations.
@@ -12,8 +12,74 @@
12
12
 
13
13
  This tool is designed for niche environments where outbound network access is restricted, such as high-security enterprises and projects that only allow limited inbound communication (e.g., SSH, RDP).
14
14
 
15
+ ## Start here
16
+
17
+ If you are new to `git-ssh-sync`, read these sections in order:
18
+
19
+ | Goal | Section |
20
+ |---|---|
21
+ | Check whether this tool fits your environment | [Who is this for?](#who-is-this-for) |
22
+ | Understand the repository layout | [Architecture](#architecture) |
23
+ | Install and run the shortest setup | [Quick start](#quick-start) |
24
+ | Register a real project | [Configuration](#configuration) |
25
+ | Work day to day | [Daily Development Workflow](#daily-development-workflow) |
26
+ | Recover from stopped synchronization | [Troubleshooting](#troubleshooting) |
27
+
28
+ The common path is:
29
+
30
+ 1. Install `git-ssh-sync` on the local machine.
31
+ 2. Register a project with `init`.
32
+ 3. Create or attach the development repositories with `clone` or `attach`.
33
+ 4. Run `pull` before editing and `push` after committing on the development environment.
34
+
35
+ ## Who is this for?
36
+
37
+ Use `git-ssh-sync` if:
38
+
39
+ - Your development environment cannot access GitHub / GitLab directly.
40
+ - Your local machine can access GitHub / GitLab.
41
+ - Your local machine can SSH into the development environment.
42
+ - You want to edit, build, test, and commit in the development environment.
43
+ - You want to synchronize by Git commits and branches instead of copying files manually.
44
+
45
+ If your development environment can already access GitHub / GitLab directly, you usually do not need this tool.
46
+
15
47
  This is not a file synchronization tool. It synchronizes Git objects and branches. Source editing, building, testing, and committing are performed in the development environment, while communication with GitHub/GitLab is handled by the local machine.
16
48
 
49
+ ## Architecture
50
+
51
+ `git-ssh-sync` keeps GitHub/GitLab access on the local machine and Git work on
52
+ the development environment.
53
+
54
+ ```text
55
+ origin: GitHub / GitLab
56
+ ↑↓
57
+ local gateway repo
58
+ ↑↓ git over SSH
59
+ dev bare cache repo
60
+ ↑↓
61
+ dev work repo
62
+ ```
63
+
64
+ Terms used throughout this document:
65
+
66
+ | Term | Meaning |
67
+ |---|---|
68
+ | `origin` | Original remote repository on GitHub / GitLab |
69
+ | `local gateway repo` | Relay repository on the local machine |
70
+ | `dev bare cache repo` | Bare repository on the development environment |
71
+ | `dev work repo` | Repository where you edit, build, test, and commit on the development environment |
72
+ | `gitsync remote` | Remote in the dev work repo that points to the dev bare cache repo |
73
+
74
+ ## Current limitations
75
+
76
+ The following features are not supported yet:
77
+
78
+ - Git LFS
79
+ - Git submodules
80
+ - automatic conflict resolution
81
+ - synchronizing uncommitted file changes
82
+
17
83
  ## Prerequisites
18
84
 
19
85
  `git-ssh-sync` assumes the following configuration:
@@ -26,20 +92,25 @@ Local machine
26
92
  Development environment
27
93
  ```
28
94
 
29
- Local machine:
95
+ | Place | Requirements |
96
+ |---|---|
97
+ | Local machine | Can access GitHub / GitLab, can SSH to the development environment, and has `git` and `uv` available |
98
+ | Development environment | Can be accessed via SSH from the local machine, has `git` available, and does not need direct GitHub / GitLab access |
30
99
 
31
- - Can access GitHub / GitLab
32
- - Can SSH to the development environment
33
- - Has `git` and `uv` available
34
- - Uses `git-ssh-sync` for commit synchronization, status checks, and diagnostics between GitHub/GitLab and the development environment
100
+ Run `git-ssh-sync` on the local machine. Edit, build, test, and commit on the
101
+ development environment. Synchronization between the two sides happens through
102
+ Git commits and branches.
35
103
 
36
- Development environment:
104
+ ## Safety model
37
105
 
38
- - Can be accessed via SSH from the local machine
39
- - Cannot directly access GitHub / GitLab from the development environment
40
- - Has `git` available
41
- - Performs source editing, building, testing, and committing
42
- - Synchronizes with GitHub/GitLab via the local machine
106
+ `git-ssh-sync` does not:
107
+
108
+ - Synchronize uncommitted files
109
+ - Automatically merge or rebase branches
110
+ - Force-push to origin
111
+ - Modify a dirty development work tree
112
+ - Require GitHub/GitLab credentials on the development environment
113
+ - Require direct outbound access from the development environment to GitHub/GitLab
43
114
 
44
115
  ## Installation
45
116
 
@@ -61,6 +132,37 @@ After installation, verify that the command can be executed.
61
132
  git-ssh-sync --help
62
133
  ```
63
134
 
135
+ ## Quick start
136
+
137
+ After installing `git-ssh-sync`, the shortest path from setup to daily sync is:
138
+
139
+ ```bash
140
+ uv tool install git-ssh-sync
141
+
142
+ git-ssh-sync init myproject \
143
+ --origin git@github.com:example/myproject.git \
144
+ --dev-host devserver \
145
+ --dev-user user \
146
+ --dev-path /home/user/work/myproject
147
+
148
+ git-ssh-sync clone myproject
149
+ git-ssh-sync doctor myproject
150
+
151
+ git-ssh-sync pull myproject
152
+
153
+ # On the development environment:
154
+ # cd ~/work/myproject
155
+ # git add .
156
+ # git commit -m "Add feature"
157
+
158
+ git-ssh-sync status myproject
159
+ git-ssh-sync push myproject
160
+ ```
161
+
162
+ Run `clone` and `doctor` for the first setup. For regular work, run `pull`
163
+ before editing, commit on the development environment, then check `status` and
164
+ run `push` from the local machine.
165
+
64
166
  ## Configuration
65
167
 
66
168
  First, register the project you want to synchronize.
@@ -218,11 +320,8 @@ git-ssh-sync clone myproject
218
320
  git-ssh-sync doctor myproject
219
321
  ```
220
322
 
221
- `clone` creates a gateway repository on your local machine and deploys cache and work repositories on the development environment.
222
-
223
- - Gateway repository: Relay repository on the local machine
224
- - Cache repository: Bare repository on the development environment
225
- - Work repository: Repository where actual editing, building, testing, and committing are performed on the development environment
323
+ `clone` creates the local gateway repo and deploys the dev bare cache repo and
324
+ dev work repo described above.
226
325
 
227
326
  Afterward, the work repository on the development environment can be used as a normal Git repository.
228
327
 
@@ -253,6 +352,16 @@ execution after reviewing the plan.
253
352
  git-ssh-sync attach myproject --yes
254
353
  ```
255
354
 
355
+ Use this table to choose between setup diagnostics, wiring repair, and recovery
356
+ after an interrupted sync.
357
+
358
+ | Situation | Command |
359
+ |---|---|
360
+ | Check initial setup or connectivity | `git-ssh-sync doctor myproject` |
361
+ | Repair missing or mismatched `gitsync` remote/cache wiring | `git-ssh-sync doctor myproject --repair` |
362
+ | Diagnose after interrupted `pull` / `push` | `git-ssh-sync recover myproject` |
363
+ | Apply only safe wiring repairs after interruption | `git-ssh-sync recover myproject --yes` |
364
+
256
365
  If only the `gitsync` remote or cache wiring is missing or mismatched, run
257
366
  `doctor --repair` to inspect and repair it through the same preflight checks.
258
367
 
@@ -466,58 +575,205 @@ When diverged, automatic resolution is not performed. Follow "Workflow When Push
466
575
 
467
576
  ## Common Commands
468
577
 
469
- ```bash
470
- # Display help
471
- git-ssh-sync --help
578
+ | Goal | Command |
579
+ |---|---|
580
+ | Display help | `git-ssh-sync --help` |
581
+ | Register a project | `git-ssh-sync init myproject --origin git@github.com:example/myproject.git --dev-host devserver --dev-user user --dev-path /home/user/work/myproject` |
582
+ | List registered project settings | `git-ssh-sync config list` |
583
+ | Show registered project settings | `git-ssh-sync config show myproject` |
584
+ | Initial clone | `git-ssh-sync clone myproject` |
585
+ | Check synchronization status | `git-ssh-sync status myproject` |
586
+ | Check branch status | `git-ssh-sync branch myproject` |
587
+ | Inspect development work repo status | `git-ssh-sync dev status myproject` |
588
+ | Inspect development work repo diff | `git-ssh-sync dev diff myproject --stat` |
589
+ | Reflect changes from origin to development environment | `git-ssh-sync pull myproject` |
590
+ | Reflect commits from development environment to origin | `git-ssh-sync push myproject` |
591
+ | Switch development environment branch | `git-ssh-sync checkout myproject feature/foo` |
592
+ | Create and switch to a new branch from a base branch | `git-ssh-sync checkout myproject -b feature/foo --base develop` |
593
+ | Diagnostics | `git-ssh-sync doctor myproject` |
594
+ | Diagnose after an interrupted sync | `git-ssh-sync recover myproject` |
595
+ | Apply safe recovery repairs | `git-ssh-sync recover myproject --yes` |
472
596
 
473
- # Register a project
474
- git-ssh-sync init myproject \
475
- --origin git@github.com:example/myproject.git \
476
- --dev-host devserver \
477
- --dev-user user \
478
- --dev-path /home/user/work/myproject
597
+ For commands with many options, prefer the full examples in the workflow
598
+ sections above. They are easier to copy safely because each option is shown on
599
+ its own line.
479
600
 
480
- # List registered project settings
481
- git-ssh-sync config list
601
+ ## Troubleshooting
482
602
 
483
- # Show registered project settings
484
- git-ssh-sync config show myproject
603
+ Use `status` first when synchronization stops or the current state is unclear.
604
+ Use `doctor` for setup, connectivity, and repository wiring problems. Use
605
+ `recover` after an interrupted `pull` or `push`.
485
606
 
486
- # Initial clone
487
- git-ssh-sync clone myproject
607
+ ### push stops because origin has new commits
488
608
 
489
- # Check synchronization status
609
+ Cause:
610
+ origin has commits that are not included in the development environment branch,
611
+ or origin and the development environment branch have diverged.
612
+
613
+ Check:
614
+
615
+ ```bash
490
616
  git-ssh-sync status myproject
617
+ ```
491
618
 
492
- # Check branch status
493
- git-ssh-sync branch myproject
619
+ Fix:
620
+
621
+ ```bash
622
+ git-ssh-sync pull myproject
623
+ # If pull cannot fast-forward, merge or rebase in the development environment.
624
+ # See "Workflow When Push Stops" for the detailed recovery flow.
625
+ ```
626
+
627
+ ### pull cannot fast-forward
494
628
 
495
- # Inspect development work repo status
629
+ Cause:
630
+ origin and the development environment branch have diverged. `git-ssh-sync`
631
+ does not perform automatic merge or automatic rebase.
632
+
633
+ Check:
634
+
635
+ ```bash
636
+ git-ssh-sync status myproject
496
637
  git-ssh-sync dev status myproject
638
+ ```
497
639
 
498
- # Inspect development work repo diff
499
- git-ssh-sync dev diff myproject --stat
640
+ Fix:
500
641
 
501
- # Reflect changes from origin to development environment
502
- git-ssh-sync pull myproject
642
+ ```bash
643
+ # On the development environment
644
+ cd ~/work/myproject
645
+ git fetch gitsync
646
+ git merge gitsync/main
647
+ # or: git rebase gitsync/main
648
+ ```
503
649
 
504
- # Reflect commits from development environment to origin
650
+ After resolving conflicts and committing or continuing the rebase, run:
651
+
652
+ ```bash
653
+ git-ssh-sync status myproject
505
654
  git-ssh-sync push myproject
655
+ ```
506
656
 
507
- # Switch development environment branch
508
- git-ssh-sync checkout myproject feature/foo
657
+ ### Development work repo is dirty
509
658
 
510
- # Create and switch to new branch from base branch
511
- git-ssh-sync checkout myproject -b feature/foo --base develop
659
+ Cause:
660
+ the development environment work repo has uncommitted changes. Uncommitted
661
+ changes are not synchronized, and repair commands do not commit, stash, merge,
662
+ or rebase them automatically.
663
+
664
+ Check:
512
665
 
513
- # Diagnostics
666
+ ```bash
667
+ git-ssh-sync dev status myproject
668
+ git-ssh-sync dev diff myproject --stat
669
+ ```
670
+
671
+ Fix:
672
+
673
+ ```bash
674
+ # On the development environment
675
+ cd ~/work/myproject
676
+ git status
677
+ git add <files-to-sync>
678
+ git commit
679
+ ```
680
+
681
+ Commit changes that should be synchronized, or stash/remove local-only changes
682
+ before running `pull`, `push`, `attach`, or `doctor --repair` again.
683
+
684
+ ### gitsync remote is missing or mismatched
685
+
686
+ Cause:
687
+ the `gitsync` remote in the development work repo does not point to the expected
688
+ bare cache repo, or the wiring is missing.
689
+
690
+ Check:
691
+
692
+ ```bash
514
693
  git-ssh-sync doctor myproject
694
+ ```
515
695
 
516
- # Diagnose and optionally repair after an interrupted sync
517
- git-ssh-sync recover myproject
518
- git-ssh-sync recover myproject --yes
696
+ Fix:
697
+
698
+ ```bash
699
+ git-ssh-sync doctor myproject --repair
700
+ git-ssh-sync doctor myproject --repair --yes
701
+ ```
702
+
703
+ ### Cache repo or work repo already exists
704
+
705
+ Cause:
706
+ `clone` was asked to create a development work repo or bare cache repo at a path
707
+ that already exists.
708
+
709
+ Check:
710
+
711
+ ```bash
712
+ git-ssh-sync doctor myproject
713
+ ```
714
+
715
+ Fix:
716
+
717
+ ```bash
718
+ git-ssh-sync attach myproject --dev-path /home/user/work/myproject
719
+ git-ssh-sync doctor myproject --repair
720
+ ```
721
+
722
+ Use `attach` when the existing repositories are intentional. Otherwise choose an
723
+ empty path or move the existing directory before running `clone` again.
724
+
725
+ ### Windows path is broken
726
+
727
+ Cause:
728
+ the local shell may consume backslashes in Windows paths before
729
+ `git-ssh-sync` receives them, or the project may be configured with the wrong
730
+ development OS.
731
+
732
+ Check:
733
+
734
+ ```bash
735
+ git-ssh-sync config show myproject
736
+ git-ssh-sync doctor myproject
519
737
  ```
520
738
 
739
+ Fix:
740
+
741
+ ```bash
742
+ git-ssh-sync init myproject \
743
+ --origin git@github.com:example/myproject.git \
744
+ --dev-host devserver \
745
+ --dev-user user \
746
+ --dev-os windows \
747
+ --dev-path 'C:\Users\user\work\myproject'
748
+ ```
749
+
750
+ Quote Windows paths that contain backslashes when running commands from macOS or
751
+ Linux shells.
752
+
753
+ ### SSH connection fails
754
+
755
+ Cause:
756
+ the local machine cannot connect to the development environment over SSH, or the
757
+ configured host, user, port, or authentication settings are incorrect.
758
+
759
+ Check:
760
+
761
+ ```bash
762
+ git-ssh-sync doctor myproject
763
+ ssh user@devserver
764
+ ```
765
+
766
+ Fix:
767
+
768
+ ```bash
769
+ git-ssh-sync config show myproject
770
+ # Update the project config or recreate it with the correct --dev-host,
771
+ # --dev-user, --dev-port, and SSH authentication settings.
772
+ ```
773
+
774
+ Run `doctor --debug` or use `--log-file` when you need the exact SSH and Git
775
+ commands used during diagnosis.
776
+
521
777
  ## Logging
522
778
 
523
779
  `git-ssh-sync` supports detailed logging for troubleshooting and monitoring synchronization operations.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "git-ssh-sync"
3
- version = "0.4.0"
3
+ version = "0.5.1"
4
4
  description = "Sync Git commits through a local machine for development environments without direct GitHub or GitLab access."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1,3 +1,3 @@
1
1
  """git-ssh-sync package."""
2
2
 
3
- __version__ = "0.4.0"
3
+ __version__ = "0.5.1"
@@ -109,7 +109,7 @@ def default_config_path() -> Path:
109
109
  if sys.platform == "win32":
110
110
  base = Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
111
111
  else:
112
- base = Path.home() / ".config"
112
+ base = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"))
113
113
  return base / "git-ssh-sync" / "config.yaml"
114
114
 
115
115
 
@@ -197,6 +197,7 @@ def remote(
197
197
  cwd: str | Path | None = None,
198
198
  env: Mapping[str, str] | None = None,
199
199
  verbose: bool = False,
200
+ check: bool = True,
200
201
  ) -> CommandResult:
201
202
  """Run `git remote`."""
202
- return run_git(["remote", *args], cwd=cwd, env=env, verbose=verbose)
203
+ return run_git(["remote", *args], cwd=cwd, env=env, verbose=verbose, check=check)
@@ -90,6 +90,31 @@ def _ensure_origin_branch(local_path: Path, branch: str) -> None:
90
90
  )
91
91
 
92
92
 
93
+ def _origin_branch_exists_on_remote(local_path: Path, branch: str) -> bool:
94
+ result = git.run_git(
95
+ ["ls-remote", "--exit-code", "--heads", "origin", branch],
96
+ cwd=local_path,
97
+ check=False,
98
+ )
99
+ if result.returncode == 0:
100
+ return True
101
+ if result.returncode == 2:
102
+ return False
103
+ raise CommandExecutionError(
104
+ environment=result.environment,
105
+ command=result.command,
106
+ returncode=result.returncode,
107
+ cwd=result.cwd,
108
+ stdout=result.stdout,
109
+ stderr=result.stderr,
110
+ )
111
+
112
+
113
+ def _ensure_origin_branch_on_remote(local_path: Path, branch: str) -> None:
114
+ if not _origin_branch_exists_on_remote(local_path, branch):
115
+ raise SyncError(f"Origin branch does not exist: {branch}")
116
+
117
+
93
118
  def _ensure_origin_branch_missing(project: str, local_path: Path, branch: str) -> None:
94
119
  if _origin_branch_exists(local_path, branch):
95
120
  raise SyncError(
@@ -99,6 +124,17 @@ def _ensure_origin_branch_missing(project: str, local_path: Path, branch: str) -
99
124
  )
100
125
 
101
126
 
127
+ def _ensure_origin_branch_missing_on_remote(
128
+ project: str, local_path: Path, branch: str
129
+ ) -> None:
130
+ if _origin_branch_exists_on_remote(local_path, branch):
131
+ raise SyncError(
132
+ f"Origin branch already exists: {branch}\n\n"
133
+ "Run checkout without --base to use the existing branch:\n\n"
134
+ f" git-ssh-sync checkout {project} {branch}"
135
+ )
136
+
137
+
102
138
  def _create_origin_branch(local_path: Path, branch: str, base_branch: str) -> None:
103
139
  git.push(
104
140
  "origin",
@@ -369,6 +405,47 @@ def _ensure_pushable(
369
405
  )
370
406
 
371
407
 
408
+ def _ensure_pushable_from_development(
409
+ project: str, project_config: ProjectConfig, local_path: Path, branch: str
410
+ ) -> None:
411
+ origin_head = git.rev_parse([f"refs/remotes/origin/{branch}"], cwd=local_path)
412
+ origin_commit = origin_head.stdout.strip()
413
+ result = ssh.run_remote_git(
414
+ project_config.dev.host,
415
+ project_config.dev.work_path,
416
+ ["merge-base", "--is-ancestor", origin_commit, f"refs/heads/{branch}"],
417
+ user=project_config.dev.user,
418
+ check=False,
419
+ remote_os=project_config.dev.os,
420
+ )
421
+ if result.returncode == 0:
422
+ return
423
+ if result.returncode == 1:
424
+ current_branch = _remote_current_branch(project_config)
425
+ current_commit = _remote_short_head(project_config)
426
+ raise SyncError(
427
+ f"Cannot push {branch}.\n\n"
428
+ f"origin/{branch} has commits that are not included in dev/{branch}.\n\n"
429
+ f"Project:\n {project}\n\n"
430
+ "Development:\n"
431
+ f" host: {project_config.dev.host}\n"
432
+ f" path: {project_config.dev.work_path}\n"
433
+ f" branch: {current_branch}\n"
434
+ f" commit: {current_commit}\n\n"
435
+ "Run:\n\n"
436
+ f" git-ssh-sync pull {project}\n\n"
437
+ "Then resolve the branch on the development environment before pushing again."
438
+ )
439
+ raise CommandExecutionError(
440
+ environment=result.environment,
441
+ command=result.command,
442
+ returncode=result.returncode,
443
+ cwd=result.cwd,
444
+ stdout=result.stdout,
445
+ stderr=result.stderr,
446
+ )
447
+
448
+
372
449
  def _load_project(project: str) -> ProjectConfig:
373
450
  return get_project(load_config(), project)
374
451
 
@@ -456,12 +533,12 @@ def checkout_project(
456
533
  operations = ["fetch origin in the gateway repository"]
457
534
  if create:
458
535
  base = base_branch or _require_remote_current_branch(project_config)
459
- _ensure_origin_branch(local_path, base)
460
- _ensure_origin_branch_missing(project, local_path, branch)
536
+ _ensure_origin_branch_on_remote(local_path, base)
537
+ _ensure_origin_branch_missing_on_remote(project, local_path, branch)
461
538
  preflight.extend(
462
539
  [
463
- f"origin/{base} exists in the gateway repository",
464
- f"origin/{branch} does not exist in the gateway repository",
540
+ f"origin/{base} exists on origin",
541
+ f"origin/{branch} does not exist on origin",
465
542
  ]
466
543
  )
467
544
  operations.extend(
@@ -470,12 +547,13 @@ def checkout_project(
470
547
  f"fetch origin/{branch} into the gateway repository",
471
548
  ]
472
549
  )
473
- _ensure_origin_branch(local_path, branch)
550
+ else:
551
+ _ensure_origin_branch_on_remote(local_path, branch)
474
552
  _ensure_dev_clean(project, project_config)
475
553
  branch_exists = _remote_branch_exists(project_config, branch)
476
554
  preflight.extend(
477
555
  [
478
- f"origin/{branch} exists in the gateway repository",
556
+ f"origin/{branch} exists on origin",
479
557
  "development work tree is clean",
480
558
  ]
481
559
  )
@@ -527,7 +605,9 @@ def push_project(project: str, *, dry_run: bool = False) -> None:
527
605
  if dry_run:
528
606
  git.run_git(["fetch", "--dry-run", "origin"], cwd=local_path)
529
607
  _ensure_origin_branch(local_path, selected_branch)
530
- _ensure_pushable(project, project_config, local_path, selected_branch)
608
+ _ensure_pushable_from_development(
609
+ project, project_config, local_path, selected_branch
610
+ )
531
611
  _print_dry_run_plan(
532
612
  project=project,
533
613
  branch=selected_branch,