├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── git-sync.sh
├── repo_settings
└── default_sync_project.sh
├── request-git-sync.sh
└── util
├── change_detector.sh
├── gawk
├── base_processing.gawk
├── change_detector.gawk
├── global.gawk
├── global_const.gawk
├── global_util.gawk
├── input_processing.gawk
├── post_fetch_processing.gawk
└── pre_fetch_processing.gawk
├── repo_create.sh
├── restore-after-crash.sh
├── set_base_logic.sh
├── set_env.sh
└── sync_pass.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | /sync-projects
2 | /repo_settings
3 | /.vscode
4 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "util/bash-git-credential-helper"]
2 | path = util/bash-git-credential-helper
3 | url = https://github.com/it3xl/bash-git-credential-helper.git
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2020, Ilya Tretyakov, it3xl.ru, git-repo-sync, https://github.com/it3xl/git-repo-sync
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # git-repo-sync
2 |
3 | ## Synchronization of Branches between Remote Git-repositories
4 |
5 | * The **git-repo-sync** is a bash script that synchronizes branches between two remote Git-repositories.
6 | * Git-tags are not synchronized.
7 | * You configure once what branches to synchronize and how.
8 | * You have to investigate some time to understand **git-repo-sync** conflict solving strategies and configuring.
9 | * Your run **git-repo-sync** periodically, preferebly every few minutes.
10 |
11 | *Warning!* Before reading the following keep in mind the difference between **local** and **remote** Git repositories.
12 |
13 | If your people push (commit) often to a single synchronized Git-branch and do it to different remote Git-repositories, then:
14 |
15 | * Run **git-repo-sync** before pushing to such the branch.
16 |
17 | ### Manual Action of Synchronization
18 |
19 | If someone pushed to the same branch (to another remote repo) in between the runing of **git-repo-sync** and your pusing. In this case:
20 |
21 | * Run **git-repo-sync**
22 | * Udpate your local repository (git fetch).
23 | * Check if your commits (push) wasn't deleted from your remote Git-repository. (FYI. You local commits in your local repository will not be changed!)
24 | * If it was deleted in the remote repo:
25 | * merge, rebase, etc., your local branch over the latest remote commits;
26 | * repeat Git-push for your branch.
27 | * Repeat everything until your pushed branch will be in expected commits in your remote Git-repository.
28 |
29 | This situation is covered by notifications but you have to configure this by yourlself in your enterprise environment.
30 |
31 | ## Use Cases
32 |
33 | * Adhesion of Git-remote-repositories of clients and software/support suppliers. Temporary or permanent.
34 | * Independence from an external remote Git repository if it is slow and could be out of service time after time.
35 | * You software teams have independent Git remote repositories.
36 |
37 | ## Requirements
38 |
39 | * Install Git
40 | * Use bash to run **git-repo-sync**. (It is not tested for zsh)
41 | * Tune any automation to run **git-repo-sync** periodically - crones, schedulers, Jenkins, GitLab-CI, etc. Or run it periodically yourself.
42 |
43 | This is enough for Windows, Arch based Linux (Manjaro), GNU based Linux
44 |
45 | ### macOS Additional Requirements
46 |
47 | * Update bash by running (restart your shell after this)
48 | * `brew install bash`
49 | * Install gAWK (GNU AWK)
50 | * `brew install gawk`
51 |
52 | ### Ubuntu Additional Requirements
53 |
54 | * Install gAWK (GNU AWK).
55 | * consider [this case](https://askubuntu.com/questions/561621/choosing-awk-version-on-ubuntu-14-04/561626#561626).
56 |
57 | ### Other Linux Additional Requirements
58 |
59 | * Check gawk presence. Run `gawk '{ exit; }'` or see https://unix.stackexchange.com/a/236666/207074
60 | * Check that bash version is 4.2 or above.
61 |
62 | ## How to use
63 |
64 | Copy **git-repo-sync** somewhere
65 |
66 | git clone https://github.com/it3xl/git-repo-sync.git
67 |
68 | Let **git-repo-sync** know location of your remote Git repositories.
69 | Modify `url_a` and `url_b` variables in [default_sync_project.sh](https://github.com/it3xl/git-repo-sync/blob/master/repo_settings/default_sync_project.sh).
70 | You can use URL-s and file paths.
71 |
72 | url_a=https://example.com/git/my_repo.git
73 |
74 | url_b='/c/my-folder/my-local-git-repo-folder'
75 |
76 | Run periodically the `git-sync.sh` file, which is located in the root of **git-repo-sync**.
77 |
78 | bash git-sync.sh
79 |
80 | The `git-sync.sh` will tell you if there are any troubles. For example you need to update awk to gAWK in Ubuntu.
81 |
82 | ## The Trade-off
83 |
84 | _The Trade-off_ is an automated Git-conflict solving logic of git-repo-sync.
85 |
86 | Even if you run **git-repo-sync** periodically and often, you still have a chance to get a Git-conflict. But a small chance.
87 | So, you must know what to do in case of Git-conflicts solved by git-repo-sync.
88 |
89 | ### Minimize chances of The Trade-off
90 |
91 | Run **git-repo-sync** before Git-pushing. I.e. synchronize your both Git-remote-repos before pushing into any of them.
92 | In this case, Git will be responsible for conflict resolution, not **git-repo-sync**.
93 |
94 | ### When git-repo-sync will be solving the conflicts.
95 |
96 | You should have the both
97 |
98 | - You run **git-repo-sync** rarely. I.e. someone aready pushed commites exactly to your branch after last running of **git-repo-sync**.
99 | - And you and your teammate have pushed changes to the same Git-branch but through different remote repositories and your remote repositories are no synchronized between your Git-pushes.
100 |
101 | Basically, you don't know about **git-repo-sync** until you are in this situation.
102 |
103 | ### Behavior of git-repo-sync in case of Git-conflicst
104 |
105 | **git-repo-sync** sees a Git-conflict and uses one of Conflict Solving strategies described below.
106 | As a result, you should provide the below steps to fix The Trade-off.
107 |
108 | ### Your steps to fix The Trade-off
109 |
110 | The main idea is "Re-push your local Git-commit in case of a conflict".
111 |
112 | - Run **git-repo-sync** to synchronize both Git-remote-repositories (if you have no periodical auto-runs).
113 |
114 | - Upload changes from your remote Git-repository to your local repository.
115 | - Check if you local commit have lost its remote counterpart. I.e. the commit exist only in your local repository.
116 | - Performe Git-merge/rebase of your local commit.
117 | - Performe Git-push of your changes.
118 |
119 | - Run **git-repo-sync** to synchronize your changes with changes from another side Git-remote-repository (if you have no periodical auto-runs).
120 |
121 | ### How do I know if there were Git-conflicts
122 |
123 | - Check it manually. This is described in the above steps.
124 | - **git-repo-sync** has notifications over plain text files. Ask your DevOps to distribute it.
125 |
126 | ## Using On Linux
127 |
128 | Run `git-sync.sh` and it will tell you what **git-repo-sync** needs.
129 | In most cases you have to install gAWK. This applies to Ubuntu.
130 | Docker Alpine Linux images require *bash* and *gAWK* to be installed.
131 | You have to update the *bash* if you use an extra old Linux distro.
132 |
133 | ## Using on Windows
134 |
135 | Ha! You're lucky. You have to do nothing and have five options to run **git-repo-sync**.
136 |
137 | Open PowerShell or CMD in the **git-repo-sync** folder and run one of three.
138 |
139 | "C:\Program Files\Git\bin\bash.exe" git-sync.sh
140 | "C:\Program Files\Git\usr\bin\bash.exe" git-sync.sh
141 | "C:\Program Files\Git\git-bash.exe" git-sync.sh
142 |
143 | Or you can reinstall Git and integrate the bash into your Windows during installation. Then run
144 |
145 | bash git-sync.sh
146 |
147 | Or you can try to update the PATH environment variable. Try to add the following (that wasn't tested by me)
148 |
149 | ;C:\Program Files\Git\cmd;C:\Program Files\Git\mingw64\bin;C:\Program Files\Git\usr\bin
150 |
151 | ## Do not synchronize all branches
152 |
153 | Despite that there are [fair cases](https://github.com/it3xl/git-repo-sync/issues/3#issuecomment-771494886) when it is useful to sync all branches, this is not always a good idea.
154 | Some well know Git-servers block some branches in different ways. Some of them create "trash"-branches which you do not want to see synchronized.
155 |
156 | So, you can synchronize branches that have special prefixes only.
157 | You could configure these prefixes in [default_sync_project.sh](https://github.com/it3xl/git-repo-sync/blob/master/repo_settings/default_sync_project.sh) configuration file.
158 | What's important, these prefixes are related to correspondent *conflict solving strategies*.
159 |
160 | ## Conflict Solving Strategies
161 |
162 | ### The Victim Strategy
163 |
164 | By default all branches are synced under this strategy.
165 | You can do whatever you want with such branches from both sides (repositories).
166 | In case of commit conflicts, any newest commit will win.
167 | You can relocate branches to any position, delete and move them back in history if you run **git-repo-sync** regularly.
168 |
169 | Use the following variable to limit branches synchronized by this strategy.
170 |
171 | victim_branches_prefix=@
172 |
173 | The most common value for victim_branches_prefix is "@".
174 | In this case only branches that start with `@` will be synchronized.
175 | E.g. `@dev`, `@dev-staging`, `@test`, `@test-staging`, `@my-feature`, etc.
176 |
177 | ### The Conventional Strategy
178 |
179 | By using this strategy you limit what your teammates may do from another side repository with branches on your side remote repository.
180 |
181 | Branches with the following prefix will be owned by the repo from [url_a](https://github.com/it3xl/git-repo-sync/blob/master/repo_settings/default_sync_project.sh) variable. Let's call it *A side*.
182 |
183 | side_a_conventional_branches_prefix=client-
184 |
185 | Branches with the following prefix will be owned by the repo from [url_b](https://github.com/it3xl/git-repo-sync/blob/master/repo_settings/default_sync_project.sh) variable. Let's call it *B side*.
186 |
187 | side_b_conventional_branches_prefix=vendor-
188 |
189 | Other examples of prefix pairs: `a-`, `b-`; `microsoft/`, `google/`; `foo-`, `bar-`;
190 |
191 | On the owning side repo: You can do whatever you want with such branches.
192 |
193 | On a repo of another side:
194 | You can do fast-forward updates and merges.
195 | You can move such branches back in Git-history if you run **git-repo-sync** periodically.
196 |
197 | All commit conflicts will be solved in favor of the owning side.
198 |
199 | ### Other Unimplemented Strategies
200 |
201 | Just propouse something interesting.
202 | BTW, the Victim and Conventional approaches cover 80% of cases you need (I beleive).
203 |
204 | ## Disaster Protection
205 |
206 | People have to make mistakes to become better. This is normal. But let's protect our clients from such the mistakes.
207 | Define *sync_enabling_branch* variable
208 |
209 | sync_enabling_branch=it3xl_git_repo_sync_enabled
210 |
211 | Its value may represent any branch name.
212 | Examples: `@test`, `client-prod`, `vendor-master`, `it3xl_git_repo_sync_enabled`.
213 |
214 | The **git-repo-sync** will check if such a branch exist in both remote repositories and that it has the same or related commits, i.e. its commits are located in the same Git-tree.
215 | This will protect you from occasional adhesion of unrelated git-repositories and deletion of branches that have the same names.
216 | Git may store many independent projects (trees) in the same repository and this is uncommon behavior for many users.
217 |
218 | I advise to use `it3xl_git_repo_sync_enabled` branch name to make this explicit for others that their remote Git-repo is synchronized with another remote repo.
219 | They could search for the word *it3xl_git_repo_sync_enabled* in the Internet and understand the applied sync solution.
220 |
221 | Be aware that a branch mentioned in the `sync_enabling_branch` variable will be alwasy synchronized by **git-repo-sync**.
222 | Probably this is not a good idea to specify here the `master` branch name because a branch mentioned in `sync_enabling_branch` will be synchronized under the Victim strategy. But you can specify there a branch with one of your conventional prefixes for the Conventional syncing of it. For example `client-master`.
223 |
224 | ## Notes, Drawbacks & Limitations
225 | * Usage with SSH isn't tested but possible.
226 | * **git-repo-sync** is resilient for HTTP fails and interruptions.
227 | * It has protections from an occasional deletion of your entire remote repository.
228 | * Arbitrary Git-history rewriting is supported.
229 | * Within a single installation, **git-repo-sync** can synchronize as many pairs of Git-repositories as you want. Every sync pair is a sync project for **git-repo-sync**.
230 | * Git-tags are not synchronized.
231 | * Remarks why: Some Git-servers block manipulations with Git-tags. Time was saved for research and covering all possible cases. Another repo's tags created issues for Git-tag based CI/CD-s.
232 | * **git-repo-sync** doesn't attempt to do Git-merge or rebase. Just FYI.
233 |
234 | ## Support Operations
235 |
236 | ### Remote Repo Replacing Support
237 |
238 | This is a real case of my customer. You may want to synchronize your existing Git-repo with a Git-repo of your new software parnter.
239 |
240 | Option 1.
241 | Create a new git-repo-sync project and use it (project description file or environment variables).
242 |
243 | Option 2.
244 | Modify your existing project. Update its description file or environment variables.
245 | Delete `git-repo-sync/sync-projects/` directory.
246 | Start synchronization as usual.
247 |
248 | Option 3.
249 | Your Git-repository is extra huge and you can't recreate it. This is a TL;DR. Ask a Git-professional for a help.
250 |
251 | ## Known Issues
252 |
253 | ### It is still untracked
254 | `Something went wrong for . It is still untracked.
255 | Possibly the program or the network were interrupted.`
256 |
257 | * Disable your antivirus or Check Point.
258 | * Check if this branch is blocked by your Git-server.
259 |
260 | ## Automation support
261 | * **git-repo-sync** works with remote Git repositories asynchronously, by default.
262 | * It works much faster under \*nix OS-es because Git-bash on Windows is slower. But compare to network latency, this is nothing.
263 | * You can separate change detection and synchronization phases of **git-repo-sync** for readability of CI/CD logs.
264 | * Multiple configuration capabilities are supported. Environment, configuration files, combination of them.
265 | * Integration with **bash Git Credential Helper - [git-cred](https://github.com/it3xl/bash-git-credential-helper)** to obtain credentials from a parent shell environment.
266 | * You shouldn't do anything in case of connectivity fails. Continue to run **git-repo-sync** periodically and everything will be restored automatically.
267 | * After every synchronization, analyze notification files to send notifications about branch deletions or commit conflict solving.
268 | See `git-repo-sync/sync-projects//file-signals/`
269 | * `notify_solving` - for conflict solving
270 | * `notify_del` - for deletions
271 | * See [instructions](https://github.com/it3xl/git-repo-sync/blob/master/repo_settings/default_sync_project.sh) on how to configure more synchronization pairs of remote Git repositories.
272 | * Number of pairs is unlimited. Every pair is a separate sync project.
273 |
274 |
--------------------------------------------------------------------------------
/git-sync.sh:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | set -euf +x -o pipefail
4 |
5 | #echo
6 | #echo Start $(basename $BASH_SOURCE)
7 |
8 | invoke_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
9 | source "$invoke_path/util/set_env.sh" "$@"
10 |
11 |
12 |
13 | echo
14 | source "$path_git_sync_util/repo_create.sh"
15 | cd "$path_sync_repo"
16 |
17 |
18 | rm -f "$env_notify_del_file"
19 | rm -f "$env_notify_solving_file"
20 |
21 | source "$path_git_sync_util/restore-after-crash.sh"
22 |
23 |
24 | source "$path_git_sync_util/sync_pass.sh"
25 | source "$path_git_sync_util/sync_pass.sh"
26 |
27 |
28 | echo
29 | echo @ RESULT: Successfully completed with $git_sync_pass_num_required sync-pass'/'es.
30 | #echo
31 | #echo End $(basename $BASH_SOURCE)
32 |
--------------------------------------------------------------------------------
/repo_settings/default_sync_project.sh:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 | #
3 | ## Post your questions on https://github.com/it3xl/git-repo-sync/issues
4 | ## I will be glad to explain the ambiguities and to improve this instruction for others.
5 |
6 |
7 | # Configure location of your remote Git repositories.
8 | #
9 | url_a=https://example.com/git/my_repo.git
10 | #
11 | url_b='/c/my-folder/my-local-git-repo-folder'
12 |
13 | # Don't sync all branches. Synchronize branches with the following prefix.
14 | #
15 | # victim_branches_prefix=@
16 |
17 | # Prevent catastrophe!
18 | # Don't sync repositories without the specified branch in the same Git-tree (same or related commits).
19 | #
20 | # sync_enabling_branch=it3xl_git_repo_sync_enabled
21 |
22 | # Limit branch manipulations for another repository side.
23 | # See "Conventional Syncing strategy" below.
24 | #
25 | # side_a_conventional_branches_prefix=client-
26 | # side_b_conventional_branches_prefix=vendor-
27 |
28 |
29 | # Automation integration. See details below
30 | #
31 | # git_sync_project_folder=my-sync-project
32 | #
33 | # use_bash_git_credential_helper=1
34 |
35 |
36 | #
37 | ##
38 | ### Descriptions & Explanations
39 | ##
40 | #
41 |
42 |
43 | ## This Configuration File
44 | #
45 | # Variables mentioned in this file configure synchronization of two remote Git-repositories.
46 | # Use the following options to configure your synchronization.
47 | #
48 | ## Uncomment and modify required variables in this file.
49 | #
50 | ## Create a copy of this file in one of the following locations
51 | # * git-repo-sync/repo_settings
52 | # * git-repo-sync/../git-repo-sync.repo_settings
53 | # Pass the name of your copied file to git-sync.sh as the first parameter for every run.
54 | # Invocation example: $ ./git-sync.sh my-sync-project.sh
55 | #
56 | ## Create a copy of this file in an arbitrary location.
57 | # Pass its full path to git-sync.sh as the first parameter for every run.
58 | # Or use a relative to "git-repo-sync" path.
59 | #
60 | ## Declare the variables in your script or environment.
61 | # Warning! Your configuration file will be ignored if your parent environment
62 | # or calling script have the git_sync_project_folder variable.
63 |
64 | ## url_a
65 | ## url_b
66 | # Let's call your two synchronized remote Git repositories as sides A and B.
67 | # Then url_a and url_b variables point to git remote repositories of the A and B sides accordingly.
68 | # It could be an URL or a file path (SSH addresses wasn't tested yet).
69 | # For paths on Windows use the following notation
70 | # url_a="/c/my-folder/my-git-local-repo-folder"
71 |
72 | ## victim_branches_prefix
73 | # Limit branches which will be synchronized under a Victim Sync strategy.
74 | # If undefined or empty then all branches will be synced.
75 | # (except conventionally prefixed branches described below)
76 | #
77 | ## The Victim Sync strategy.
78 | # You can do whatever you want with such branches from both remote sides (repositories).
79 | # In case of commit conflicts, any newest commit will win.
80 | # You can relocate branches to any position or delete them, etc.
81 | # You can move a branch back in history if you sync your repos regularly.
82 | #
83 | # The most common value is "@".
84 | # Examples: @dev, @dev-staging, @test, @test-staging
85 |
86 | ## sync_enabling_branch
87 | # Represents any branch name.
88 | # The git-repo-sync will check that such a branch exist in both remote repositories
89 | # and that it has the same or related commits, i.e. located in the same Git-tree.
90 | # This will protect you from occasional adhesion of unrelated git-repositories.
91 | # Git may store many independent projects in the same repository and this is uncommon behavior for many users.
92 | #
93 | # We advise to use it3xl_git_repo_sync_enabled name to make it explicit to others that their Git-repo is syncing with another remote repo.
94 | # Examples: @test, client-prod, vendor-master, it3xl_git_repo_sync_enabled
95 |
96 | ## side_a_conventional_branches_prefix
97 | # Branches with a prefix from this variable will be owned by the repo from "url_a". Let's call it A side.
98 | #
99 | ## side_b_conventional_branches_prefix
100 | # Branches with a prefix from this variable will be owned by the repo from "url_b". Let's call it B side.
101 | #
102 | # Branches with such prefixes will be updated under the Conventional Sync strategy.
103 | # You can define both or one variable.
104 | #
105 | ## The Conventional Sync strategy
106 | # On a repo of the owning side: You can do whatever you want with such branches.
107 | # On a repo of another side: You can do fast-forward updates and merges.
108 | # You can move such a branch back in Git-history from an non-owning side if you run git-repo-sync regularly.
109 | # All commit conflicts will be solved in favor of the owning side.
110 | #
111 | # Example of prefix pairs: client-, vendor-; a-, b-; microsoft/, google/
112 |
113 |
114 | ## git_sync_project_folder
115 | # It defines a folder in which your sync-project artifacts will be stored inside of "git-repo-sync/sync-projects/".
116 | # In the absence of git_sync_project_folder, its value will be taken from the name of a provided configuration file.
117 | #
118 | # Warning! Your configuration file will be ignored if your parent environment
119 | # or calling script have this variable. In this case, all required variables must be configured in the parent environment.
120 |
121 |
122 | ## use_bash_git_credential_helper=1
123 | # This variables enables using of the "git-cred", the "bash Git Credential Helper" from https://github.com/it3xl/bash-git-credential-helper
124 | #
125 | # Git-cred allows you to use Git-credential values from environment variables
126 | # which are defined automatically by any Continues Integration (CI/CD) tool.
127 | #
128 | # You can use "git-cred" as an external tool and tune everything manually.
129 | # But configuring it here allows you to initialize git-cred only once.
130 | # BTW, "git-cred" allows a free relocation of your installation "git-repo-sync" folder.
131 | #
132 | # * Before using git-cred you must complete the following steps.
133 | #
134 | # ** Load Git sub-modules inside of your "git-repo-sync" folder (https://github.com/it3xl/git-repo-sync)
135 | #
136 | # ** Before any call to "git-sync.sh" or to "request-git-sync.sh", define the following environment variables in your CI/CD automation server or tool
137 | # For the repo in $url_a
138 | # git_cred_username_repo_a=some-login
139 | # git_cred_password_repo_a=some-password
140 | # For the repo in $url_b
141 | # git_cred_username_repo_b=another-login
142 | # git_cred_password_repo_b=another-password
143 | #
144 | # ** Assign use_bash_git_credential_helper variable to 1.
145 |
146 |
147 |
--------------------------------------------------------------------------------
/request-git-sync.sh:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | set -euf +x -o pipefail
4 |
5 | echo
6 | echo Start $(basename $BASH_SOURCE)
7 |
8 | invoke_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
9 | source "$invoke_path/util/set_env.sh" "$@"
10 |
11 | rm -f "$env_modifications_signal_file"
12 | rm -f "$env_modifications_signal_file_a"
13 | rm -f "$env_modifications_signal_file_b"
14 |
15 | echo
16 | source "$path_git_sync_util/repo_create.sh"
17 | cd "$path_sync_repo"
18 |
19 | source "$path_git_sync_util/restore-after-crash.sh"
20 |
21 | source "$path_git_sync_util/change_detector.sh"
22 |
23 | echo
24 | if (( $changes_detected == 1 )); then
25 | install -D /dev/null "$env_modifications_signal_file"
26 | install -D /dev/null "$env_modifications_signal_file_a"
27 | install -D /dev/null "$env_modifications_signal_file_b"
28 |
29 | # Passing of remote refs to prevent excessive network requesting.
30 | echo "$remote_refs_a" >> "$env_modifications_signal_file_a"
31 |
32 | echo "$remote_refs_b" >> "$env_modifications_signal_file_b"
33 |
34 | echo '@' RESULT: Synchronization requested.
35 | else
36 | echo '@' RESULT: Refs are the same. Exit.
37 | echo
38 |
39 | # exit
40 | fi
41 |
42 |
43 | echo
44 | echo End $(basename $BASH_SOURCE)
45 |
--------------------------------------------------------------------------------
/util/change_detector.sh:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | if [[ $env_allow_async == 1 ]]; then
4 | echo '! Async (async remote refs checks are used)'
5 |
6 | mkdir -p "$path_async_output"
7 |
8 | git ls-remote --heads "$url_a" $sync_ref_specs > "$path_async_output/remote_refs_a.txt" &
9 | pid_remote_refs_a=$!
10 | git ls-remote --heads "$url_b" $sync_ref_specs > "$path_async_output/remote_refs_b.txt" &
11 | pid_remote_refs_b=$!
12 |
13 | err_remote_refs_a=0;
14 | wait $pid_remote_refs_a || err_remote_refs_a=$?
15 | err_remote_refs_b=0;
16 | wait $pid_remote_refs_b || err_remote_refs_b=$?
17 |
18 | remote_refs_a=$(<"$path_async_output/remote_refs_a.txt")
19 | remote_refs_b=$(<"$path_async_output/remote_refs_b.txt")
20 |
21 | if (( $err_remote_refs_a != 0 )); then
22 | echo
23 | echo "> Async fail | Change detection | $origin_a | Error $err_remote_refs_a"
24 | echo "$remote_refs_a"
25 | echo ">"
26 | fi;
27 | if (( $err_remote_refs_b != 0 )); then
28 | echo
29 | echo "> Async fail | Change detection | $origin_b | Error $err_remote_refs_b"
30 | echo "$remote_refs_b"
31 | echo ">"
32 | fi;
33 | if (( $err_remote_refs_a != 0 )); then
34 | echo
35 | echo "> Exit."
36 | exit $err_remote_refs_a;
37 | fi;
38 | if (( $err_remote_refs_b != 0 )); then
39 | echo
40 | echo "> Exit."
41 | exit $err_remote_refs_b;
42 | fi;
43 | else
44 | echo '! Sync (sync remote refs checks are used)'
45 |
46 | remote_refs_b=$(git ls-remote --heads "$url_b" $sync_ref_specs)
47 | remote_refs_a=$(git ls-remote --heads "$url_a" $sync_ref_specs)
48 | fi;
49 |
50 |
51 | track_refs_a=$(git for-each-ref --format="%(objectname) %(refname)" $track_refspecs_a)
52 | track_refs_b=$(git for-each-ref --format="%(objectname) %(refname)" $track_refspecs_b)
53 |
54 | ## remote_count=$(echo "$remote_refs_a" | awk 'END { print NR }';)
55 | ## track_count=$(echo "$track_refs_a" | awk 'END { print NR }';)
56 |
57 | export remote_refs_a;
58 | export remote_refs_b;
59 | export track_refs_a;
60 | export track_refs_b;
61 |
62 | changes_detected=$($env_awk_edition --file="$path_git_sync_util/gawk/change_detector.gawk")
63 | echo
64 |
65 |
--------------------------------------------------------------------------------
/util/gawk/base_processing.gawk:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | @include "global.gawk"
4 |
5 |
6 | function unlock_deletion(){
7 | if(remote_empty[side_both]){
8 | deletion_blocked_by = "deletion-blocked:remotes-are-empty"
9 | return;
10 | }
11 | if(remote_empty[side_a]){
12 | deletion_blocked_by = "deletion-blocked:a-remote-is-empty"
13 | return;
14 | }
15 | if(remote_empty[side_b]){
16 | deletion_blocked_by = "deletion-blocked:b-remote-is-empty"
17 | return;
18 | }
19 |
20 | deletion_allowed = 1;
21 | }
22 | function generate_missing_refs( ref){
23 | for(ref in refs){
24 | if(!refs[ref][side_a][remote][ref_key]){
25 | refs[ref][side_a][remote][ref_key] = remote_refs_path ref;
26 | }
27 | if(!refs[ref][side_b][remote][ref_key]){
28 | refs[ref][side_b][remote][ref_key] = remote_refs_path ref;
29 | }
30 | if(!refs[ref][side_a][track][ref_key]){
31 | refs[ref][side_a][track][ref_key] = track_refs_path origin[side_a] "/" ref;
32 | }
33 | if(!refs[ref][side_b][track][ref_key]){
34 | refs[ref][side_b][track][ref_key] = track_refs_path origin[side_b] "/" ref;
35 | }
36 |
37 | # d_trace("ref is " ref);
38 | # d_trace("track ref_key side_a " refs[ref][side_a][track][ref_key]);
39 | # d_trace("track ref_key side_b " refs[ref][side_b][track][ref_key]);
40 | # d_trace("remote ref_key side_a " refs[ref][side_a][remote][ref_key]);
41 | # d_trace("remote ref_key side_b " refs[ref][side_b][remote][ref_key]);
42 | }
43 | }
44 |
45 | function append_by_side(side, host, addition){
46 | host[side] = host[side] (host[side] ? newline_substitution : "") addition;
47 | }
48 | function append_by_val(host, addition){
49 | host[val_key] = host[val_key] (host[val_key] ? newline_substitution : "") addition;
50 | }
51 |
52 | function use_victim_sync(ref){
53 | return !use_conv_sync(ref);
54 | }
55 |
56 | function use_conv_sync(ref) {
57 | return side_a_conv_ref(ref) || side_b_conv_ref(ref);
58 | }
59 |
60 | function side_a_conv_ref(ref){
61 | return side_conv_ref(ref, side_a);
62 | }
63 |
64 | function side_b_conv_ref(ref){
65 | return side_conv_ref(ref, side_b);
66 | }
67 |
68 | function side_conv_ref(ref, side, conv_pref){
69 | conv_pref = prefix[side];
70 | if(!conv_pref){
71 | return 0;
72 | }
73 |
74 | return index(ref, conv_pref) == 1;
75 | }
76 |
77 | function explicit_victim_ref(ref){
78 | if(!pref_victim){
79 | return 0;
80 | }
81 |
82 | return index(ref, pref_victim) == 1;
83 | }
84 |
85 | function sync_all_refs(){
86 | return !pref_victim;
87 | }
88 |
--------------------------------------------------------------------------------
/util/gawk/change_detector.gawk:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | @include "global.gawk"
4 |
5 |
6 | BEGIN {
7 | write_after_line("> change detecting");
8 |
9 | sync_enabling_branch = ENVIRON["sync_enabling_branch"];
10 |
11 | parse_refs("remote_refs_a", remote, side_a);
12 | parse_refs("remote_refs_b", remote, side_b);
13 | parse_refs("track_refs_a", track, side_a);
14 | parse_refs("track_refs_b", track, side_b);
15 |
16 | exit;
17 | }
18 |
19 | function parse_refs(env_var, dest_key, side, split_arr, ind, val, split_val, sha, ref){
20 | split(ENVIRON[env_var], split_arr, "\n");
21 |
22 | for(ind in split_arr){
23 | val = split_arr[ind];
24 | if(!val){
25 | continue;
26 | }
27 |
28 | split(val, split_val, " ");
29 |
30 | sha = split_val[1];
31 | ref = split_val[2];
32 | sub(/^refs\/heads\/|^refs\/remotes\/[^\/]+\//, "", ref);
33 | if(!ref || !sha){
34 | continue;
35 | }
36 |
37 | refs[ref][side][dest_key][sha_key] = sha;
38 | }
39 | }
40 |
41 |
42 | END {
43 | process_emptiness();
44 |
45 | if(side_empty[side_both]){
46 | write("both-repos-are-empty")
47 | }
48 | block_sync();
49 |
50 | changed = 0;
51 |
52 | for (ref in refs) {
53 | a_remote = refs[ref][side_a][remote][sha_key]
54 | a_track = refs[ref][side_a][track][sha_key]
55 |
56 | b_remote = refs[ref][side_b][remote][sha_key]
57 | b_track = refs[ref][side_b][track][sha_key]
58 |
59 | if(!a_remote || !a_track || !b_remote || !b_track){
60 | changed = 1;
61 | trace(ref " has empty sha; On" (a_track?"":" a_track") (b_track?"":" b_track") (a_remote?"":" a_remote") (b_remote?"":" b_remote"))
62 | continue;
63 | }
64 |
65 | if(a_remote != b_remote \
66 | || a_track != b_track \
67 | || a_remote != a_track){
68 | changed = 1;
69 | trace(ref " has unequal sha")
70 | continue;
71 | }
72 | }
73 |
74 | print changed;
75 |
76 | write("> change detecting end");
77 | }
78 |
79 | function block_sync( ref){
80 | if(side_empty[side_both]){
81 | return;
82 | }
83 |
84 | ref = sync_enabling_branch;
85 |
86 | if(!ref){
87 | return;
88 | }
89 |
90 | _block_sync_by_side(refs[ref][side_a][remote][sha_key], side_a);
91 | _block_sync_by_side(refs[ref][side_b][remote][sha_key], side_b);
92 | }
93 |
94 | function _block_sync_by_side(remote_sha, side){
95 | if(remote_sha){
96 | return;
97 | }
98 | if(remote_empty[side]){
99 | return;
100 | }
101 |
102 | write("Syncing is blocked as \"" sync_enabling_branch "\" branch doesn't exist in the \""side"\" remote repo");
103 |
104 | exit 92;
105 | }
106 |
107 |
--------------------------------------------------------------------------------
/util/gawk/global.gawk:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | @include "global_const.gawk"
4 | @include "global_util.gawk"
5 |
6 |
7 | function process_emptiness( not_empty_remote, remote_sha, not_empty_track, track_sha){
8 | for (side in sides) {
9 | for (ref in refs) {
10 | remote_sha = refs[ref][side][remote][sha_key];
11 | if(!remote_sha)
12 | continue;
13 |
14 | not_empty_remote[side] = 1;
15 | break;
16 | }
17 | }
18 |
19 | remote_empty[side_a] = !not_empty_remote[side_a]
20 | remote_empty[side_b] = !not_empty_remote[side_b]
21 | remote_empty[side_any] = remote_empty[side_a] || remote_empty[side_b];
22 | remote_empty[side_both] = remote_empty[side_a] && remote_empty[side_b];
23 |
24 |
25 | for (side in sides) {
26 | for (ref in refs) {
27 | track_sha = refs[ref][side][track][sha_key];
28 | if(!track_sha)
29 | continue;
30 |
31 | not_empty_track[side] = 1;
32 | break;
33 | }
34 | }
35 |
36 | track_empty[side_a] = !not_empty_track[side_a]
37 | track_empty[side_b] = !not_empty_track[side_b]
38 | track_empty[side_any] = track_empty[side_a] || track_empty[side_b];
39 | track_empty[side_both] = track_empty[side_a] && track_empty[side_b];
40 |
41 | side_empty[side_a] = remote_empty[side_a] && track_empty[side_a];
42 | side_empty[side_b] = remote_empty[side_b] && track_empty[side_b];
43 | side_empty[side_any] = side_empty[side_a] || side_empty[side_b];
44 | side_empty[side_both] = side_empty[side_a] && side_empty[side_b];
45 |
46 | attach_mode = remote_empty[side_any] && ! track_empty[side_any];
47 | }
48 |
--------------------------------------------------------------------------------
/util/gawk/global_const.gawk:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | BEGIN { # Constants.
4 | track_refs_path = "refs/remotes/";
5 | remote_refs_path = "refs/heads/";
6 |
7 | remote = "remote-key"
8 | track = "track-key"
9 | all_track = "all-track-key"
10 |
11 | sha_key = "sha-key";
12 | ref_key = "ref-key";
13 |
14 | side_a = "A";
15 | side_b = "B";
16 |
17 | side_any = "side-any-key";
18 | side_both = "side-both-key";
19 |
20 | sides[side_a] = side_a;
21 | sides[side_b] = side_b;
22 |
23 | asides[side_a] = sides[side_b]
24 | asides[side_b] = sides[side_a]
25 |
26 | val_key = "a-value-key";
27 | common = "common-key";
28 | equal = "equal-key";
29 | empty_both = "empty-both-key";
30 | empty_any = "empty-any-key";
31 |
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/util/gawk/global_util.gawk:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | BEGIN { # Constants.
4 | out_stream_attached = "/dev/stderr";
5 | }
6 | BEGIN {
7 | awk_trace_on = !! ENVIRON["env_awk_trace_on"];
8 | }
9 |
10 |
11 | function write(msg){
12 | print msg >> out_stream_attached;
13 | }
14 | function write_after_line(msg){
15 | write("\n" msg);
16 | }
17 | function trace(msg){
18 | if(!awk_trace_on)
19 | return;
20 |
21 | if(!msg){
22 | print "|" >> out_stream_attached;
23 | return;
24 | }
25 |
26 | print "|" msg >> out_stream_attached;
27 | }
28 | function trace_header(msg){
29 | trace();
30 | trace(msg);
31 | trace();
32 | }
33 | function trace_after_line(msg){
34 | trace();
35 | trace(msg);
36 | }
37 | function trace_line(msg){
38 | trace(msg);
39 | trace();
40 | }
41 | function d_trace(msg){ # Development trace.
42 | if(0)
43 | return;
44 |
45 | trace("|~" msg)
46 | }
47 |
48 | END{ # Disposing.
49 | # Possibly the close here is excessive.
50 | #https://www.gnu.org/software/gawk/manual/html_node/Close-Files-And-Pipes.html#Close-Files-And-Pipes
51 | close(out_stream_attached);
52 | }
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/util/gawk/input_processing.gawk:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | @include "global.gawk"
4 |
5 |
6 | BEGIN { # Constants.
7 | }
8 | BEGIN { # Parameters.
9 | initial_states_processing();
10 | }
11 | function initial_states_processing( side, split_arr, split_val, ind, ref, val, sha){
12 | sync_enabling_branch = ENVIRON["sync_enabling_branch"];
13 |
14 | origin_a = ENVIRON["origin_a"];
15 | if(!origin_a){
16 | write("Error. Parameter origin_a is empty");
17 | exit 82;
18 | }
19 | origin[side_a] = origin_a;
20 | origin_a = ""
21 |
22 | origin_b = ENVIRON["origin_b"];
23 | if(!origin_b){
24 | write("Error. Parameter origin_b is empty");
25 | exit 83;
26 | }
27 | origin[side_b] = origin_b;
28 | origin_b = ""
29 |
30 | pref_a_conv = ENVIRON["pref_a_conv"];
31 | prefix[side_a] = pref_a_conv;
32 | pref_a_conv = ""
33 |
34 | pref_b_conv = ENVIRON["pref_b_conv"];
35 | prefix[side_b] = pref_b_conv;
36 | pref_b_conv = ""
37 |
38 | pref_victim = ENVIRON["pref_victim"];
39 |
40 | newline_substitution = ENVIRON["env_awk_newline_substitution"];
41 | if(!newline_substitution){
42 | write("Error. Parameter newline_substitution is empty");
43 | exit 86;
44 | }
45 |
46 | split(ENVIRON["conv_move"], split_arr, "\n");
47 | for(ind in split_arr){
48 | val = split_arr[ind];
49 |
50 | split(val, split_val, " ");
51 |
52 | ref = split_val[1];
53 | sha = split_val[2];
54 | if(!ref || !sha){
55 | continue;
56 | }
57 |
58 | conv_move[ref][sha];
59 | }
60 |
61 | split(ENVIRON["victim_move"], split_arr, "\n");
62 | for(ind in split_arr){
63 | val = split_arr[ind];
64 |
65 | split(val, split_val, " ");
66 |
67 | ref = split_val[1];
68 | sha = split_val[2];
69 | if(!ref || !sha){
70 | continue;
71 | }
72 |
73 | victim_move[ref][sha];
74 | }
75 | }
76 |
77 |
78 | BEGINFILE { # Preparing processing for every portion of refs.
79 | file_states_processing();
80 | }
81 | function file_states_processing() {
82 | current_dest = "";
83 | current_side = "";
84 | ref_prefix = "";
85 | switch (++file_num) {
86 | case 1:
87 | current_dest = remote;
88 | current_side = side_a;
89 | ref_prefix = remote_refs_path;
90 | break;
91 | case 2:
92 | current_dest = remote;
93 | current_side = side_b;
94 | ref_prefix = remote_refs_path;
95 | break;
96 | case 3:
97 | current_dest = track;
98 | current_side = side_a;
99 | ref_prefix = track_refs_path origin[side_a] "/";
100 | break;
101 | case 4:
102 | current_dest = track;
103 | current_side = side_b;
104 | ref_prefix = track_refs_path origin[side_b] "/";
105 | break;
106 | }
107 | }
108 | { # Ref states preparation.
109 | if(!$2){
110 | # Empty input stream of an empty refs' variable or an empty line.
111 | next;
112 | }
113 |
114 | prepare_ref_states();
115 | }
116 | function prepare_ref_states( ref){
117 | prefix_name_key();
118 |
119 | ref = $3;
120 | if( !sync_all_refs() \
121 | && ref != sync_enabling_branch \
122 | && !explicit_victim_ref(ref) \
123 | && !side_a_conv_ref(ref) \
124 | && !side_b_conv_ref(ref) \
125 | ){
126 | trace("!unexpected " $2 " in " current_dest " " $1 "; branch name (" ref ") has no allowed prefixes or not allowed");
127 |
128 | next;
129 | }
130 |
131 | sync_refs[ref] = ref;
132 | refs[ref][current_side][current_dest][sha_key] = $1;
133 | refs[ref][current_side][current_dest][ref_key] = $2;
134 | }
135 | function prefix_name_key( refspec_split) { # Generates a common key for all 4 locations of every ref.
136 | $3 = $2
137 | split($3, refspec_split, ref_prefix);
138 | $3 = refspec_split[2];
139 | }
140 |
141 |
142 | END {
143 | process_emptiness();
144 | }
145 |
--------------------------------------------------------------------------------
/util/gawk/post_fetch_processing.gawk:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | @include "base_processing.gawk"
4 |
5 |
6 | BEGIN {
7 | write_after_line("> main processing");
8 | }
9 |
10 | @include "input_processing.gawk"
11 |
12 |
13 | END {
14 | main_processing();
15 | write("> main processing end");
16 | }
17 |
18 | function main_processing( ref){
19 | generate_missing_refs();
20 |
21 | unlock_deletion();
22 | if(!deletion_allowed){
23 | write(deletion_blocked_by);
24 | }
25 |
26 | for(ref in refs){
27 | state_to_action(ref);
28 | }
29 | actions_to_operations();
30 | operations_to_refspecs();
31 | process_excluded_tracks();
32 | refspecs_to_stream();
33 | }
34 | function state_to_action(ref, remote_sha, track_sha, side, is_victim, ref_type){
35 | if(attach_mode)
36 | return;
37 | # if(remote_empty[side_any])
38 | # return;
39 |
40 | for(side in sides){
41 | remote_sha[side] = refs[ref][side][remote][sha_key];
42 | track_sha[side] = refs[ref][side][track][sha_key];
43 | }
44 |
45 | remote_sha[equal] = remote_sha[side_a] == remote_sha[side_b];
46 | track_sha[equal] = track_sha[side_a] == track_sha[side_b];
47 |
48 | if(remote_sha[equal] && track_sha[equal] && track_sha[side_a] == remote_sha[side_b])
49 | return;
50 |
51 | remote_sha[common] = remote_sha[equal] ? remote_sha[side_a] : "";
52 | remote_sha[empty_both] = !(remote_sha[side_a] || remote_sha[side_b]);
53 | remote_sha[empty_any] = !remote_sha[side_a] || !remote_sha[side_b];
54 |
55 | track_sha[common] = track_sha[equal] ? track_sha[side_a] : "";
56 | track_sha[empty_both] = !(track_sha[side_a] || track_sha[side_b]);
57 | track_sha[empty_any] = !track_sha[side_a] || !track_sha[side_b];
58 |
59 | is_victim = use_victim_sync(ref);
60 |
61 | if(remote_sha[empty_both]){
62 | # A branch in the ref was deleted manually in both repos.
63 |
64 | trace(ref ": action-remove-tracking-as-unknown-on-both-remotes");
65 | a_remove_tracking_both[ref];
66 |
67 | return;
68 | }
69 |
70 | # ! All further actions assume that remote refs are unequal.
71 |
72 | if(track_sha[empty_both]){
73 | trace("!Warning!");
74 | trace("!! Something went wrong for " ref ". It is still untracked.");
75 | trace("!! Possibly the program or the network were interrupted.");
76 | trace("!! We will try to sync it later (during the second sync pass).");
77 |
78 | return;
79 | }
80 |
81 | if(del_to_action(ref, is_victim, remote_sha, track_sha)){
82 | return;
83 | }
84 | if(move_to_refspec_by_state(ref, conv_move, is_victim)){
85 | return;
86 | }
87 | if(move_to_refspec_by_state(ref, victim_move, is_victim)){
88 | return;
89 | }
90 | if(victim_move_to_refspec(ref, remote_sha, track_sha, is_victim)){
91 | return;
92 | }
93 | if(ff_move_by_remote(ref, remote_sha, track_sha)){
94 | return;
95 | }
96 |
97 | ref_type = is_victim ? "victim" : "conv";
98 | trace(ref ": action-solve-all-others." ref_type "; it has different track or/and remote branch commits");
99 | if(is_victim){
100 | a_victim_solve[ref];
101 | }else{
102 | a_conv_solve[ref];
103 | }
104 | }
105 | function del_to_action(ref, is_victim, remote_sha, track_sha, side, aside, deletion_state, unowned_ref, action_key){
106 | if(!track_sha[equal]){
107 | return;
108 | }
109 |
110 | for(side in sides){
111 | aside = asides[side];
112 |
113 | deletion_state = !remote_sha[side] && remote_sha[aside] == track_sha[common];
114 | if(!deletion_state){
115 | continue;
116 | }
117 |
118 | if(is_victim){
119 | if(deletion_allowed){
120 | trace(ref ": action-del-victim on " aside "; it is disappeared from " side);
121 | a_del[aside][ref];
122 | }else{
123 | trace(ref ": action-del-blocked-victim-restore on " aside "; disappeared from " side);
124 | a_restore_del[ref];
125 | }
126 |
127 | return 1;
128 | }
129 |
130 | unowned_ref = side_conv_ref(ref, aside);
131 |
132 | if(!deletion_allowed || unowned_ref){
133 | action_key = "action-del-blocked-conv";
134 | if(unowned_ref){
135 | action_key = action_key "&unowned-ref";
136 | }
137 |
138 | trace(ref ": " action_key " on " aside "; disappeared from " side);
139 | a_conv_solve[ref];
140 | append_by_val(out_notify_solving, "conventional-restore-as-del-blocked | " side " | " ref " | " action_key " | restoring-to:" side ":" refs[ref][side][track][sha_key]);
141 |
142 | return 1;
143 | }
144 |
145 | trace(ref ": action-del-conv on " aside "; it is disappeared from " side);
146 | a_del[aside][ref];
147 |
148 | return 1;
149 | }
150 | }
151 | function move_to_refspec_by_state(ref, source_refs, is_victim, ref_item, action_sha, cmd, parent_sha, parent_side, child_side, action_key, moving_back, owner_action, force_key){
152 | for(ref_item in source_refs){
153 | if(ref_item != ref){
154 | continue;
155 | }
156 | for(action_sha in source_refs[ref_item]){}
157 |
158 | break;
159 | }
160 | if(ref_item != ref){
161 | return;
162 | }
163 |
164 |
165 | cmd = "git merge-base " refs[ref][side_a][track][ref_key] " " refs[ref][side_b][track][ref_key];
166 |
167 |
168 | cmd | getline parent_sha;
169 | close(cmd);
170 |
171 | if(parent_sha == refs[ref][side_a][track][sha_key]){
172 | parent_side = side_a;
173 | } else if(parent_sha == refs[ref][side_b][track][sha_key]){
174 | parent_side = side_b;
175 | } else if(!parent_sha && ref == sync_enabling_branch){
176 | write("\nSyncing is blocked. You are trying to sync unrelated Git-remote repositories");
177 | write("\"" ref "\" has different SHA and has no a parent commit");
178 | write("\"" ref "\" located in " side_a ":" refs[ref][side_a][remote][sha_key] " vs " side_b ":" refs[ref][side_b][remote][sha_key]);
179 |
180 | exit 99;
181 | } else {
182 | # We didn't covered Git multi root cases by this logic yet.
183 | trace(ref ":~ rejected-move-by-state; " side_a " & " side_b " lost ff-inheritance at " parent_sha);
184 |
185 | return;
186 | }
187 |
188 | child_side = asides[parent_side];
189 | action_key = "action-fast-forward-by-state";
190 |
191 | moving_back = parent_sha == action_sha;
192 | if(moving_back){
193 | if(!is_victim){
194 | owner_action = side_conv_ref(ref, parent_side);
195 | if(!owner_action){
196 | # Moving back from a non-owner side is forbidden.
197 | trace(ref ":~ forbidden-moving-back-by-state; non-owner side cannot move conventional ref back");
198 |
199 | return;
200 | }
201 | }
202 | parent_side = child_side;
203 | child_side = asides[parent_side];
204 | force_key = "+";
205 | action_key = "action-moving-back-by-state";
206 |
207 | append_by_val(out_notify_solving, "moving-back-by-state | " parent_side " | " ref " | out-of " parent_side ":" refs[ref][parent_side][track][sha_key] " to " child_side ":" refs[ref][child_side][track][sha_key]);
208 | }
209 |
210 | trace(ref ": " action_key "; out-of " parent_side ":" refs[ref][parent_side][track][sha_key] " to " child_side ":" refs[ref][child_side][track][sha_key]);
211 | out_push[parent_side] = out_push[parent_side] " " force_key refs[ref][child_side][track][ref_key] ":" refs[ref][parent_side][remote][ref_key];
212 |
213 | # Let's inform a calling logic that we've processed the current ref.
214 | return 1;
215 | }
216 | function victim_move_to_refspec(ref, remote_sha, track_sha, is_victim, ref_item, action_sha, source_side, target_side){
217 | if(!is_victim)
218 | return;
219 |
220 | for(ref_item in victim_move){
221 | if(ref_item != ref){
222 | continue;
223 | }
224 | for(action_sha in victim_move[ref_item]){}
225 |
226 | break;
227 | }
228 | if(ref_item != ref){
229 | return;
230 | }
231 |
232 | # The idea below is to ignore a candidate ref if sha was changed since last check.
233 | # E.g. process with ra==ta & rb==tb & ra!=rb
234 |
235 | if(remote_sha[equal])
236 | return;
237 | if(track_sha[equal])
238 | return;
239 |
240 | if(remote_sha[side_a] != track_sha[side_a])
241 | return;
242 | if(remote_sha[side_b] != track_sha[side_b])
243 | return;
244 |
245 | if(remote_sha[side_a] != action_sha && remote_sha[side_b] != action_sha)
246 | return;
247 |
248 | if(remote_sha[side_a] == action_sha){
249 | source_side = side_a;
250 | } else if(remote_sha[side_b] == action_sha){
251 | source_side = side_b;
252 | } else {
253 | return;
254 | }
255 |
256 | target_side = asides[source_side];
257 |
258 | if(action_sha != refs[ref][source_side][remote][sha_key]){
259 | write("\nError! The victim_move_to_refspec sync logic brocken.");
260 | write("\"" action_sha "\" must be " refs[ref][source_side][remote][sha_key]);
261 |
262 | cmd = "need_interrupt_app"
263 | while (0 < (cmd | getline fail_text)){
264 | write(fail_text)
265 | }
266 | close(cmd);
267 |
268 | if(fail_text == "need-interrupt-app"){
269 | exit 97;
270 | }
271 | }
272 | trace(ref ": action-victim-nff-move; out-of " target_side ":" refs[ref][target_side][remote][sha_key] " to " source_side ":" refs[ref][source_side][remote][sha_key]);
273 | out_push[target_side] = out_push[target_side] " +" refs[ref][source_side][track][ref_key] ":" refs[ref][target_side][remote][ref_key];
274 |
275 | append_by_val(out_notify_solving, "victim-non-fast-forward-move | " target_side " | " ref " | out-of " target_side ":" refs[ref][target_side][remote][sha_key] " to " source_side ":" refs[ref][source_side][remote][sha_key]);
276 |
277 | # Let's inform a calling logic that we've processed the current ref.
278 | return 1;
279 | }
280 | function ff_move_by_remote(ref, remote_sha, track_sha, cmd, parent_sha, parent_side, child_side){
281 | # Process when ra==ta & rb==tb & ra!=rb & all not empty.
282 | if(remote_sha[empty_any])
283 | return;
284 | if(track_sha[empty_any])
285 | return;
286 | if(remote_sha[equal])
287 | return;
288 | if(track_sha[equal])
289 | return;
290 | if(remote_sha[side_a] != track_sha[side_a])
291 | return;
292 | if(remote_sha[side_b] != track_sha[side_b])
293 | return;
294 |
295 | cmd = "git merge-base " refs[ref][side_a][track][ref_key] " " refs[ref][side_b][track][ref_key];
296 |
297 |
298 | cmd | getline parent_sha;
299 | close(cmd);
300 |
301 | if(parent_sha == refs[ref][side_a][track][sha_key]){
302 | parent_side = side_a;
303 | } else if(parent_sha == refs[ref][side_b][track][sha_key]){
304 | parent_side = side_b;
305 | } else if(!parent_sha && ref == sync_enabling_branch){
306 | write("\nYou are trying to sync unrelated Git-remote repositories. Syncing is blocked");
307 | write("\"" ref "\" has different SHA and has no a parent commit");
308 | write("\"" ref "\" located in " side_a ":" refs[ref][side_a][remote][sha_key] " vs " side_b ":" refs[ref][side_b][remote][sha_key]);
309 |
310 | exit 98;
311 | } else {
312 | # We didn't covered Git multy root cases by this logic yet.
313 | trace(ref ":~ rejected-fast-forward-by-remote; " side_a " & " side_b " lost ff-inheritance at " parent_sha);
314 |
315 | return;
316 | }
317 |
318 | child_side = asides[parent_side];
319 |
320 | trace(ref ": action-fast-forward-by-remote; out-of " parent_side ":" refs[ref][parent_side][remote][sha_key] " to " child_side ":" refs[ref][child_side][track][sha_key]);
321 | out_push[parent_side] = out_push[parent_side] " " refs[ref][child_side][track][ref_key] ":" refs[ref][parent_side][remote][ref_key];
322 |
323 | # Let's inform a calling logic that we've processed the current ref.
324 | return 1;
325 | }
326 | function actions_to_operations( side, aside, ref, attach_both, track_sha, another_track_sha, remote_sha, ref_owner){
327 | if(attach_mode){
328 | attach_both = remote_empty[side_both];
329 | for(side in sides){
330 | if(!remote_empty[side]){
331 | continue
332 | }
333 | for(ref in refs){
334 | track_sha = refs[ref][side][track][sha_key];
335 | if(!track_sha){
336 | continue;
337 | }
338 | if(attach_both){
339 | op_push_attach_from_track[side][ref];
340 | append_by_val(out_notify_solving, "attach-both-empty-sides | " side " | " ref " | restoring-to " side ":" track_sha);
341 | }else{
342 | aside = asides[side];
343 | another_track_sha = refs[ref][aside][track][sha_key];
344 |
345 | op_push_attach_from_another[side][ref];
346 | append_by_val(out_notify_solving, "attach-empty-side | " side " | " ref " | restoring-to " aside ":" another_track_sha);
347 | }
348 | }
349 | }
350 | }
351 |
352 | for(ref in a_remove_tracking_both){
353 | for(side in sides){
354 | track_sha = refs[ref][side][track][sha_key];
355 | if(!track_sha){
356 | continue;
357 | }
358 | op_remove_both_tracking[side][ref];
359 | append_by_val(out_notify_solving, "tracking-removed | " side " | " ref " | " side ":" track_sha);
360 | }
361 | }
362 | for(ref in a_restore_del){
363 | for(side in sides){
364 | remote_sha = refs[ref][side][remote][sha_key]
365 | if(remote_sha){
366 | continue;
367 | }
368 | op_push_restore_from_track[side][ref];
369 |
370 | track_sha = refs[ref][side][track][sha_key];
371 | append_by_val(out_notify_solving, "victim-restore-as-del-blocked | " side " | " ref " | restoring-to:" side ":" track_sha);
372 | }
373 | }
374 |
375 | for(side in a_del){
376 | for(ref in a_del[side]){
377 | op_del_track[ref];
378 | op_push_del[side][ref];
379 | }
380 | }
381 |
382 | for(side in sides){
383 | aside = asides[side];
384 | for(ref in a_conv_solve){
385 | ref_owner = side_conv_ref(ref, side);
386 |
387 | if(!ref_owner){
388 | continue;
389 | }
390 |
391 | if(refs[ref][side][remote][sha_key]){
392 | op_conv_push_nff[aside][ref];
393 | } else if(refs[ref][aside][remote][sha_key]){
394 | op_conv_push_nff[side][ref];
395 | }
396 | }
397 | }
398 | }
399 | function operations_to_refspecs( side, aside, ref){
400 | for(side in op_remove_both_tracking){
401 | for(ref in op_remove_both_tracking[side]){
402 | out_remove_tracking = out_remove_tracking " " origin[side] "/" ref;
403 | }
404 | }
405 |
406 | for(side in sides){
407 | for(ref in op_del_track){
408 | if(refs[ref][side][track][sha_key]){
409 | out_remove_tracking = out_remove_tracking " " origin[side] "/" ref;
410 | }
411 | }
412 | }
413 |
414 | for(side in op_push_attach_from_track){
415 | for(ref in op_push_attach_from_track[side]){
416 | out_push[side] = out_push[side] " +" refs[ref][side][track][ref_key] ":" refs[ref][side][remote][ref_key];
417 | }
418 | }
419 |
420 | for(side in op_push_attach_from_another){
421 | aside = asides[side];
422 | for(ref in op_push_attach_from_another[side]){
423 | out_push[side] = out_push[side] " +" refs[ref][aside][track][ref_key] ":" refs[ref][side][remote][ref_key];
424 | }
425 | }
426 |
427 | for(side in op_push_restore_from_track){
428 | for(ref in op_push_restore_from_track[side]){
429 | out_push[side] = out_push[side] " +" refs[ref][side][track][ref_key] ":" refs[ref][side][remote][ref_key];
430 | }
431 | }
432 |
433 | for(side in op_push_restore_from_another){
434 | aside = asides[side];
435 | for(ref in op_push_restore_from_another[side]){
436 | out_push[side] = out_push[side] " +" refs[ref][aside][track][ref_key] ":" refs[ref][side][remote][ref_key];
437 | }
438 | }
439 |
440 | for(side in op_push_del){
441 | for(ref in op_push_del[side]){
442 | out_push[side] = out_push[side] " +:" refs[ref][side][remote][ref_key];
443 |
444 | append_by_val(out_notify_del, "deletion | " side " | " ref " | " refs[ref][side][remote][sha_key]);
445 | }
446 | }
447 |
448 | for(side in op_conv_push_nff){
449 | aside = asides[side];
450 | for(ref in op_conv_push_nff[side]){
451 | out_push[side] = out_push[side] " +" refs[ref][aside][track][ref_key] ":" refs[ref][side][remote][ref_key];
452 |
453 | if(refs[ref][side][remote][sha_key]){
454 | append_by_val(out_notify_solving, "conventional-conflict-solving | " side " | " ref " | out-of " side ":" refs[ref][side][remote][sha_key] " to " aside ":" refs[ref][aside][remote][sha_key]);
455 | }
456 | }
457 | }
458 | set_victim_refspec();
459 |
460 | # We may use post fetching as workaround for network fails and program interruptions.
461 | # Also FF-updating may fail in case of rare conflicting with a remote repo.
462 | # Without the post fetching these cases will not be resolved ever.
463 | # But we don't use it for now as we migrated to preprocessing git fetching.
464 | for(side in op_post_fetch){
465 | for(ref in op_post_fetch[side]){
466 | out_post_fetch[side] = out_post_fetch[side] " +" refs[ref][side][remote][ref_key] ":" refs[ref][side][track][ref_key];
467 | }
468 | }
469 | }
470 |
471 | function set_victim_refspec( ref, remote_sha_a, track_sha_a, trace_action, track_sha_a_txt, remote_sha_b, track_sha_b, track_sha_b_txt, cmd, newest_sha, side_winner, side_victim, victim_sha){
472 | for(ref in a_victim_solve){
473 | # We expects that "no sha" cases will be processed by common NFF-solving actions.
474 | # But this approach with variables help to solve severe errors. Also it makes code more resilient.
475 |
476 | remote_sha_a = refs[ref][side_a][remote][sha_key];
477 | track_sha_a = refs[ref][side_a][track][sha_key];
478 |
479 | remote_sha_b = refs[ref][side_b][remote][sha_key];
480 | track_sha_b = refs[ref][side_b][track][sha_key];
481 |
482 | # d_trace("a " ref "; track_sha_a:" track_sha_a "; remote_sha_a:" remote_sha_a);
483 | # d_trace("b " ref "; track_sha_b:" track_sha_b "; remote_sha_b:" remote_sha_b);
484 |
485 | if(track_sha_a && track_sha_b){
486 | # Have both tracks.
487 | cmd = "git rev-list " refs[ref][side_a][track][ref_key] " " refs[ref][side_b][track][ref_key] " --max-count=1"
488 | cmd | getline newest_sha;
489 | close(cmd);
490 | } else if(track_sha_a){
491 | newest_sha = track_sha_a;
492 | } else if(track_sha_b){
493 | newest_sha = track_sha_b;
494 | }
495 |
496 | if(newest_sha == track_sha_a){
497 | side_winner = side_a
498 | side_victim = side_b
499 | } else if(newest_sha == track_sha_b){
500 | side_winner = side_b
501 | side_victim = side_a
502 | } else {
503 | trace("unexpected behavior during victim solving | " ref);
504 |
505 | continue;
506 | }
507 |
508 | out_push[side_victim] = out_push[side_victim] " +" refs[ref][side_winner][track][ref_key] ":" refs[ref][side_victim][remote][ref_key];
509 |
510 | victim_sha = refs[ref][side_victim][remote][sha_key];
511 | # Do not show solving for new branch creation.
512 | if(victim_sha){
513 | append_by_val(out_notify_solving, "victim-conflict-solving | " side_victim " | " ref " | out-of " victim_sha " to " side_winner ":" refs[ref][side_winner][remote][sha_key]);
514 | }
515 |
516 | trace_action = victim_sha ? "victim-conflict-solving" : "victim-empty-solving";
517 |
518 | track_sha_a_txt = track_sha_a ? track_sha_a : "";
519 | track_sha_b_txt = track_sha_b ? track_sha_b : "";
520 | trace(trace_action ": " ref " on " side_winner " beat " side_victim " with " track_sha_a_txt " vs " track_sha_b_txt);
521 | }
522 | }
523 |
524 | function process_excluded_tracks( side, ref){
525 | parse_excluded_tracks("all_track_refs_a", side_a);
526 | parse_excluded_tracks("all_track_refs_b", side_b);
527 |
528 | for(ref in excluded_track_state){
529 | for(side in sides){
530 | trace(ref ": action-remove-excluded-track;" side ":" excluded_track_state[ref][side][sha_key] "; not under sync any more")
531 | }
532 | }
533 |
534 | }
535 | function parse_excluded_tracks(track_env_var, side, split_arr, ind, val, split_val, sha, refspec, refspec_split, track_ref_path, ref){
536 | split(ENVIRON[track_env_var], split_arr, "\n");
537 | for(ind in split_arr){
538 | val = split_arr[ind];
539 |
540 | split(val, split_val, " ");
541 |
542 | sha = split_val[1];
543 | refspec = split_val[2];
544 | if(!sha || !refspec){
545 | continue;
546 | }
547 |
548 | # refs/remotes/origin_x/
549 | track_ref_path = track_refs_path origin[side] "/"
550 |
551 | split(refspec, refspec_split, track_ref_path);
552 | ref = refspec_split[2];
553 |
554 | if(sync_refs[ref]){
555 | continue;
556 | }
557 |
558 | excluded_track_state[ref][side][sha_key] = sha;
559 |
560 | out_remove_tracking = out_remove_tracking " " origin[side] "/" ref;
561 | }
562 | }
563 |
564 | function refspecs_to_stream( out_processing_requested){
565 |
566 | out_processing_requested = 0 < length(out_remove_tracking out_push[side_a] out_push[side_b] out_post_fetch[side_a] out_post_fetch[side_b]);
567 |
568 |
569 | print out_processing_requested;
570 |
571 | print out_remove_tracking;
572 | print out_notify_del[val_key];
573 |
574 | print out_push[side_a];
575 | print out_push[side_b];
576 | print out_notify_solving[val_key];
577 |
578 | print out_post_fetch[side_a];
579 | print out_post_fetch[side_b];
580 |
581 | # Must print finishing line otherwise previous empty lines will be ignored by mapfile command in bash.
582 | print "{[end-of-results]}"
583 | }
584 |
585 |
586 |
--------------------------------------------------------------------------------
/util/gawk/pre_fetch_processing.gawk:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | @include "base_processing.gawk"
4 |
5 |
6 | BEGIN {
7 | write_after_line("> pre processing");
8 | }
9 |
10 | @include "input_processing.gawk"
11 |
12 |
13 | END {
14 | main_processing();
15 | write("> pre processing end");
16 | }
17 | function main_processing( ref){
18 | generate_missing_refs();
19 |
20 | for(ref in refs){
21 | state_to_action(ref);
22 | }
23 | actions_to_refspecs();
24 | refspecs_to_stream();
25 | }
26 | function state_to_action(ref, remote_sha, track_sha, side, is_victim){
27 | for(side in sides){
28 | remote_sha[side] = refs[ref][side][remote][sha_key];
29 | track_sha[side] = refs[ref][side][track][sha_key];
30 | }
31 |
32 | remote_sha[equal] = remote_sha[side_a] == remote_sha[side_b];
33 | track_sha[equal] = track_sha[side_a] == track_sha[side_b];
34 |
35 | if(remote_sha[equal] && track_sha[equal] && track_sha[side_a] == remote_sha[side_b])
36 | return;
37 |
38 | remote_sha[common] = remote_sha[equal] ? remote_sha[side_a] : "";
39 | remote_sha[empty_both] = !(remote_sha[side_a] || remote_sha[side_b]);
40 | remote_sha[empty_any] = !remote_sha[side_a] || !remote_sha[side_b];
41 |
42 | track_sha[common] = track_sha[equal] ? track_sha[side_a] : "";
43 | track_sha[empty_both] = !(track_sha[side_a] || track_sha[side_b]);
44 | track_sha[empty_any] = !track_sha[side_a] || !track_sha[side_b];
45 |
46 | is_victim = use_victim_sync(ref);
47 |
48 | if(remote_sha[empty_both])
49 | return;
50 |
51 | request_victim_move(ref, remote_sha, track_sha, is_victim);
52 |
53 | if(remote_sha[equal]){
54 | request_update_tracking(ref, remote_sha, track_sha);
55 |
56 | return;
57 | }
58 |
59 | if(track_sha[empty_both]){
60 | request_update_tracking(ref, remote_sha, track_sha);
61 |
62 | return;
63 | }
64 |
65 | request_update_tracking(ref, remote_sha, track_sha);
66 |
67 | request_conv_move(ref, remote_sha, track_sha, is_victim);
68 | }
69 | function request_update_tracking(ref, remote_sha, track_sha){
70 | for(side in sides){
71 | if(!remote_sha[side]){
72 | # No an update source.
73 | continue;
74 | }
75 | if(remote_sha[side] == track_sha[side]){
76 | # No need to update. Tracking and remote refs are the same.
77 | continue;
78 | }
79 |
80 | # Possibly gitSync or the network was interrupted.
81 | # Or this ref was moved back.
82 | # But the default scenario is that the remote ref was modified.
83 | # Let's update the tracking ref.
84 | trace(ref ": action-fetch from " side "; " ((track_sha[side]) ? "track ref is outdated" : "track ref is unknown"));
85 | a_fetch[side][ref];
86 | }
87 | }
88 | function request_conv_move(ref, remote_sha, track_sha, is_victim, side, aside, ref_owner){
89 | if(is_victim)
90 | return;
91 |
92 | if(!track_sha[equal])
93 | return;
94 |
95 | if(track_sha[empty_both])
96 | return;
97 |
98 | for(side in sides){
99 |
100 | # d_trace(ref_owner " " side " " ref " " prefix[side]);
101 |
102 | if(!remote_sha[side]){
103 | continue;
104 | }
105 |
106 | if(remote_sha[side] == track_sha[side]){
107 | continue;
108 | }
109 |
110 | aside = asides[side];
111 | if(remote_sha[aside] != track_sha[aside]){
112 | continue;
113 | }
114 |
115 | # Let's allow updating of the another side conventional refs. Remember fast-forward updating candidates.
116 | trace(ref ": action-check-conventional-move; outdated on " side);
117 | a_conv_move[ref][remote_sha[side]];
118 | }
119 | }
120 | function request_victim_move(ref, remote_sha, track_sha, is_victim, side, aside){
121 | # This allows to decrease Git local request for regulary synced repos.
122 |
123 | if(!is_victim)
124 | return;
125 |
126 | if(remote_sha[empty_any])
127 | return;
128 | if(track_sha[empty_any])
129 | return;
130 |
131 | if(remote_sha[equal])
132 | return;
133 |
134 | if(!track_sha[equal])
135 | return;
136 |
137 | # We expect that request_update_tracking will request required here fetching.
138 |
139 | for(side in sides){
140 |
141 | if(remote_sha[side] == track_sha[side]){
142 | continue;
143 | }
144 |
145 | aside = asides[side];
146 | if(remote_sha[aside] != track_sha[aside]){
147 | continue;
148 | }
149 |
150 | # Let's allow updating of the another side conventional refs. Remember fast-forward updating candidates.
151 | trace(ref ": action-request-non-fast-forward; ref changed only on " side);
152 | a_victim_move[ref][remote_sha[side]];
153 | }
154 | }
155 |
156 | function actions_to_refspecs( side, aside, ref){
157 | for(side in a_fetch){
158 | for(ref in a_fetch[side]){
159 | out_fetch[side] = out_fetch[side] " +" refs[ref][side][remote][ref_key] ":" refs[ref][side][track][ref_key];
160 | }
161 | }
162 |
163 | for(ref in a_conv_move){
164 | for(sha in a_conv_move[ref]){
165 | append_by_val(out_conv_move, ref " " sha);
166 | }
167 | }
168 |
169 | for(ref in a_victim_move){
170 | for(sha in a_victim_move[ref]){
171 | append_by_val(out_victim_move, ref " " sha);
172 | }
173 | }
174 | }
175 |
176 | function refspecs_to_stream(){
177 | print out_fetch[side_a];
178 | print out_fetch[side_b];
179 |
180 | print out_conv_move[val_key];
181 | print out_victim_move[val_key];
182 |
183 | # Must print finishing line otherwise previous empty lines will be ignored by mapfile command in bash.
184 | print "{[end-of-results]}"
185 | }
186 |
187 |
188 |
--------------------------------------------------------------------------------
/util/repo_create.sh:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | set -euf +x -o pipefail
4 |
5 |
6 | function delete_project_repo_and_exit() {
7 | echo Deletion of "$path_sync_repo" to allow restart git-cred initializing.
8 | rm -rf "$path_sync_repo"
9 |
10 | echo Interruption on error.
11 |
12 | exit 1;
13 | }
14 |
15 | function create_sync_repo(){
16 | if [[ -f "$path_sync_repo/.git/config" ]]; then
17 | return
18 | fi
19 |
20 | echo @ `basename "$BASH_SOURCE"` started
21 |
22 | mkdir -p "$path_sync_repo"
23 |
24 | # Using --quiet to prevent "hint: Using 'master' as the name for the initial branch..."
25 | git -C "$path_sync_repo" init --quiet
26 |
27 | git -C "$path_sync_repo" config --local advice.pushUpdateRejected false
28 | #git -C "$path_sync_repo" config --local core.logAllRefUpdates
29 |
30 | git -C "$path_sync_repo" remote add $origin_a "$url_a"
31 | git -C "$path_sync_repo" remote add $origin_b "$url_b"
32 |
33 |
34 | [[ "$use_bash_git_credential_helper" == "1" ]] && {
35 | echo "Initializing git-cred as use_bash_git_credential_helper is set to $use_bash_git_credential_helper"
36 |
37 | if [[ ! -f "$git_cred" ]]; then
38 | echo
39 | echo Error! Exit! You have to update/download Git-SubModules of '"'git-repo-sync'"' project to use
40 | echo the '"'bash-git-credential-helper'"' from $git_cred
41 | echo Run '"'git submodule update --init --recursive'"' in the root folder of '"'git-repo-sync'"'.
42 | echo Or comment out '"'use_bash_git_credential_helper=1'"' line in your sync project settings file.
43 | echo
44 |
45 | delete_project_repo_and_exit
46 | fi
47 |
48 | GIT_CRED_DO_NOT_EXIT=1
49 |
50 | # The it3xl/bash-git-credential-helper requires the working directory to be set to our Git-repository.
51 | cd "$path_sync_repo"
52 |
53 | source "$git_cred" init repo_a $url_a
54 | [[ $GIT_CRED_FAILED != 0 ]] && delete_project_repo_and_exit
55 |
56 | source "$git_cred" init repo_b $url_b
57 | [[ $GIT_CRED_FAILED != 0 ]] && delete_project_repo_and_exit
58 | }
59 |
60 |
61 | echo Repo created at $path_sync_repo
62 |
63 | echo @ `basename "$BASH_SOURCE"` ended
64 | }
65 | create_sync_repo
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/util/restore-after-crash.sh:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | # Puts only error output to a variable. Prevents useless execution by "--count=1".
4 | # Looks inside "refs/remotes" only.
5 | errors_for_each=$(git for-each-ref --count=1 refs/remotes 2>&1 1>/dev/null)
6 |
7 |
8 | if [[ $errors_for_each == 'warning: ignoring broken ref refs/remotes/'* ]]; then
9 | echo
10 | echo
11 | echo '@@' Crash Recovery '@@'
12 | echo '@'
13 | echo '@' Possibly the internal checking repository is slightly broken after an unexpected PC switching off.
14 | echo '@' All remote references will be reloaded. So, you will see an increased log related to internal references updating.
15 | echo '@' It is because we got the following problem':'
16 | echo "@ $errors_for_each"
17 | echo '@'
18 | echo '@@@@@@@@@@@@@@@@@@@@'
19 | echo
20 |
21 | rm -rf "$(git rev-parse --absolute-git-dir)/refs/remotes"
22 | mkdir "$(git rev-parse --absolute-git-dir)/refs/remotes"
23 | fi;
24 |
--------------------------------------------------------------------------------
/util/set_base_logic.sh:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 |
--------------------------------------------------------------------------------
/util/set_env.sh:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | # echo;echo Start `basename "$BASH_SOURCE"`
4 |
5 | [[ ${git_sync_env_initialized:+var_is_not_empty} ]] || {
6 |
7 | function need_interrupt_app(){
8 | func=process_need_interrupt_app
9 | [[ "$(type -t $func)" == 'function' ]] && {
10 | $func
11 | } || {
12 | echo "@@ Error! Application interruption wasn't processed."
13 | echo "@@ Be aware of the above error."
14 | }
15 | }; export -f need_interrupt_app
16 |
17 | function git_fail(){
18 | operation=$1
19 | origin=$2
20 | exit_code=$3
21 | info=${4:-}
22 |
23 | func=process_git_fail
24 | [[ "$(type -t $func)" == 'function' ]] && {
25 | $func $operation $origin $exit_code $info
26 | } || {
27 | echo "@@ git-operation-failed: git $operation $origin; with $exit_code exit code; $info"
28 | }
29 | }; export -f git_fail
30 |
31 | function git_sync_env_run_settings_script(){
32 |
33 | local file_name_repo_settings="${1-}"
34 |
35 | [[ ! "$file_name_repo_settings" ]] && {
36 | file_name_repo_settings="default_sync_project.sh"
37 |
38 | echo "Info. No configuration file in the first parameter. $file_name_repo_settings file will be used."
39 | }
40 |
41 | relative_settings_file="$path_git_sync/$file_name_repo_settings"
42 | relative_sibling_settings_file="$path_git_sync/../git-repo-sync.repo_settings/$file_name_repo_settings"
43 |
44 | absolute_settings_file="$file_name_repo_settings"
45 | subfolder_settings_file="$path_git_sync/repo_settings/$file_name_repo_settings"
46 |
47 |
48 | if [[ -f "$relative_settings_file" ]]; then
49 | echo Settings. Using relative config file. $relative_settings_file
50 | source "$relative_settings_file"
51 | elif [[ -f "$relative_sibling_settings_file" ]]; then
52 | echo Settings. Injecting sibling relative config file. $relative_sibling_settings_file
53 | source "$relative_sibling_settings_file"
54 | elif [[ -f "$absolute_settings_file" ]]; then
55 | echo Settings. Using absolute config file. $absolute_settings_file
56 | source "$absolute_settings_file"
57 | elif [[ -f "$subfolder_settings_file" ]]; then
58 | echo Settings. Using repo_settings subfolder config file. $subfolder_settings_file
59 | source "$subfolder_settings_file"
60 | else
61 | echo "Error! Exit! The first parameter must be an absolute path, relative path or a name of a file with your sync-project repo settings."
62 | echo The '"'$file_name_repo_settings'"' is not recognized as a file.
63 |
64 | exit 101;
65 | fi
66 |
67 | env_project_folder=$(basename ${file_name_repo_settings%.*})
68 | }
69 |
70 | function git_sync_env_init(){
71 |
72 | git_sync_env_initialized=$(date +%T)
73 | export git_sync_env_initialized
74 |
75 | export path_git_sync="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
76 | export path_git_sync_util="$path_git_sync/util"
77 |
78 | export env_awk_edition=${env_awk_edition:-gawk}
79 |
80 | type $env_awk_edition 2> /dev/null || {
81 | echo
82 | echo "Error! Exit!"
83 | echo
84 | echo " @ Our tool is optimized to work with gawk. I.e. GNU Awk (env_awk_edition = $env_awk_edition)"
85 | echo " @ You need to install gawk as we didn't adopted other AWK editions yet."
86 | edition_of_awk=$(awk -W version 2> /dev/null | head -n 1)
87 | echo " @ Your current awk is - '$edition_of_awk'"
88 |
89 | echo
90 | echo " @ Run the gawk command yourself as some shells show a hint on how to install it."
91 | echo
92 |
93 | exit 102;
94 | }
95 |
96 | # AWKPATH is env variable of GAWK that is used by the @include directive.
97 | # We need to set AWKPATH because our current directory commonly points points out to the sync Git repo, not our GAWK scripts.
98 | export AWKPATH="$path_git_sync_util/gawk"
99 |
100 | if [[ ${git_sync_project_folder:+1} ]]; then
101 | echo 'Info. Taking configuration from a parent environment as git_sync_project_folder is defined'
102 |
103 | env_project_folder=$git_sync_project_folder
104 | else
105 | echo 'Info. Seeking a configuration file provided in the first parameter as git_sync_project_folder isn''t defined'
106 |
107 | git_sync_env_run_settings_script "$@"
108 | fi
109 |
110 | if [[ ! ${url_a:+1} ]]; then missed_repo_settings+="url_a "; fi
111 | if [[ ! ${url_b:+1} ]]; then missed_repo_settings+="url_b "; fi
112 |
113 | if [[ ${missed_repo_settings:+1} ]]; then
114 | echo "Error! Exit! The following repo properties must be set: $missed_repo_settings";
115 |
116 | exit 103;
117 | fi
118 |
119 |
120 | sync_enabling_branch=${sync_enabling_branch:-}
121 |
122 | pref_a_conv=${side_a_conventional_branches_prefix:-}
123 | pref_b_conv=${side_b_conventional_branches_prefix:-}
124 |
125 | # If this var is empty, then we ignore the Victim branches functionality and its "The latest action wins" conflict solving strategy.
126 | pref_victim=${victim_branches_prefix:-}
127 |
128 | conventional_prefixes_trace_values="
129 | pref_a_conv is '$pref_a_conv'
130 | pref_b_conv is '$pref_b_conv'"
131 |
132 | if [[ "$pref_a_conv" && "$pref_a_conv" == "$pref_b_conv" ]]; then
133 | echo "Error! Exit! We expected you to assign different values for conventional ref prefixes. $conventional_prefixes_trace_values"
134 |
135 | exit 104;
136 | fi;
137 |
138 | prefixes_trace_values="
139 | pref_victim is '$pref_victim' $conventional_prefixes_trace_values"
140 |
141 | if [[ "$pref_victim" \
142 | && ( "$pref_a_conv" == "$pref_victim" \
143 | || "$pref_b_conv" == "$pref_victim" ) ]];
144 | then
145 | echo "Error! Exit! We expect that the victim ref prefix have letters different from conventional ref prefixes. $prefixes_trace_values"
146 |
147 | exit 105;
148 | fi;
149 |
150 | export origin_a=origin_a
151 | export origin_b=origin_b
152 |
153 | all_tracks_refspec_a="refs/remotes/$origin_a"
154 | all_tracks_refspec_b="refs/remotes/$origin_b"
155 |
156 | if [[ "$pref_victim" ]]; then
157 | sync_ref_specs="${pref_a_conv:+${pref_a_conv}* }${pref_b_conv:+${pref_b_conv}* }${pref_victim:+${pref_victim}* }$sync_enabling_branch"
158 |
159 | track_refspecs_a="${pref_a_conv:+refs/remotes/$origin_a/${pref_a_conv}* }`
160 | `${pref_b_conv:+refs/remotes/$origin_a/${pref_b_conv}* }`
161 | `${pref_victim:+refs/remotes/$origin_a/${pref_victim}* }`
162 | `${sync_enabling_branch:+refs/remotes/$origin_a/$sync_enabling_branch}"
163 |
164 | track_refspecs_b="${pref_a_conv:+refs/remotes/$origin_b/${pref_a_conv}* }`
165 | `${pref_b_conv:+refs/remotes/$origin_b/${pref_b_conv}* }`
166 | `${pref_victim:+refs/remotes/$origin_b/${pref_victim}* }`
167 | `${sync_enabling_branch:+refs/remotes/$origin_b/$sync_enabling_branch}"
168 | else
169 | echo 'Info. *All-branches-sync mode! Use "..._prefix" configuration parameters to limit synced branches.'
170 |
171 | sync_ref_specs=;
172 | track_refspecs_a=$all_tracks_refspec_a
173 | track_refspecs_b=$all_tracks_refspec_b
174 | fi
175 |
176 | export sync_ref_specs
177 |
178 | export track_refspecs_a
179 | export track_refspecs_b
180 |
181 | export pref_a_conv
182 | export url_a
183 | export pref_b_conv
184 | export url_b
185 | export pref_victim
186 | export sync_enabling_branch
187 |
188 | export use_bash_git_credential_helper=${use_bash_git_credential_helper-}
189 |
190 | export git_sync_pass_num=0
191 | export git_sync_pass_num_required=0
192 | export post_fetch_processing_num=0
193 |
194 | # The way we receive data from gawk we can't use new line char in the output. So we are using a substitution.
195 | export env_awk_newline_substitution='|||||'
196 |
197 | env_allow_async=${env_allow_async:-1}
198 | # env_allow_async=0
199 | export env_allow_async
200 |
201 | env_trace_refs=${env_trace_refs:-0}
202 | # env_trace_refs=1
203 | export env_trace_refs
204 |
205 | env_allow_multiple_sync_passes=${env_allow_multiple_sync_passes:-0}
206 |
207 | # These vars can be used for debugging and testing purposes.
208 | export env_awk_trace_on=1
209 | export env_process_if_refs_are_the_same=0
210 |
211 | path_project_root="$path_git_sync/sync-projects/$env_project_folder"
212 | export path_sync_repo="$path_project_root/sync_repo"
213 | # Catches outputs of the fork-join async implementation.
214 | export path_async_output="$path_project_root/async_output"
215 | signal_files_folder=file-signals
216 | export env_modifications_signal_file="$path_project_root/$signal_files_folder/there-are-modifications"
217 | export env_modifications_signal_file_a="$path_project_root/$signal_files_folder/there-are-modifications_a"
218 | export env_modifications_signal_file_b="$path_project_root/$signal_files_folder/there-are-modifications_b"
219 | export env_notify_del_file="$path_project_root/$signal_files_folder/notify_del"
220 | export env_notify_solving_file="$path_project_root/$signal_files_folder/notify_solving"
221 |
222 | export git_cred="$path_git_sync_util/bash-git-credential-helper/git-cred.sh"
223 |
224 | }
225 | git_sync_env_init "$@"
226 |
227 | source "$path_git_sync_util/set_base_logic.sh"
228 | }
229 |
230 |
231 |
232 | # echo End `basename "$BASH_SOURCE"`
233 |
--------------------------------------------------------------------------------
/util/sync_pass.sh:
--------------------------------------------------------------------------------
1 | # it3xl.ru git-repo-sync https://github.com/it3xl/git-repo-sync
2 |
3 | function sync_pass(){
4 | ((++git_sync_pass_num))
5 |
6 | (( 1 < git_sync_pass_num )) && [[ "$env_allow_multiple_sync_passes" = '0' ]] && return;
7 |
8 | if (( ${changes_detected:-1} != 1 )); then
9 | # echo '@' Previous sync-pass din not find any changes.
10 |
11 | if [[ $env_process_if_refs_are_the_same != 1 ]]; then
12 | # echo '@' Sync-pass $git_sync_pass_num was interrupted as refs are equal
13 | return;
14 | fi;
15 | fi
16 |
17 | if [[ ! -f "$env_modifications_signal_file" ]]; then
18 | source "$path_git_sync_util/change_detector.sh"
19 |
20 | if (( $changes_detected != 1 )); then
21 | echo '@' RESULT: Refs are the same.
22 |
23 | if [[ $env_process_if_refs_are_the_same != 1 ]]; then
24 | echo '@@' Sync-pass $git_sync_pass_num was interrupted as refs are equal
25 | return;
26 | fi;
27 | fi
28 | else
29 | changes_detected=1
30 |
31 | echo '@' RESULT: Synchronization requested.
32 |
33 | remote_refs_a=$(<"$env_modifications_signal_file_a")
34 | remote_refs_b=$(<"$env_modifications_signal_file_b")
35 |
36 | rm -f "$env_modifications_signal_file"
37 | rm -f "$env_modifications_signal_file_a"
38 | rm -f "$env_modifications_signal_file_b"
39 | fi
40 |
41 |
42 | ((++git_sync_pass_num_required))
43 | echo "! Running $git_sync_pass_num_required sync pass"
44 |
45 | track_refs_a=$(git for-each-ref --format="%(objectname) %(refname)" $track_refspecs_a)
46 | track_refs_b=$(git for-each-ref --format="%(objectname) %(refname)" $track_refspecs_b)
47 |
48 | if [[ $env_trace_refs == 1 ]]; then
49 | echo
50 | echo remote_refs_a=
51 | echo "$remote_refs_a"
52 | echo remote_refs_b=
53 | echo "$remote_refs_b"
54 | echo track_refs_a=
55 | echo "$track_refs_a"
56 | echo track_refs_b=
57 | echo "$track_refs_b"
58 | fi;
59 |
60 | # $env_awk_edition --file="$path_git_sync/../proto/proto.gawk" <(echo)
61 | # exit
62 |
63 |
64 | pre_fetch_processing='pre_fetch_processing.gawk'
65 | pre_proc_data=$($env_awk_edition \
66 | --file="$path_git_sync_util/gawk/$pre_fetch_processing" \
67 | <(echo "$remote_refs_a") \
68 | <(echo "$remote_refs_b") \
69 | <(echo "$track_refs_a") \
70 | <(echo "$track_refs_b") \
71 | )
72 |
73 | if [[ $env_trace_refs == 1 ]]; then
74 | echo
75 | echo pre_proc_data is
76 | echo "$pre_proc_data"
77 | fi;
78 | # exit
79 |
80 | mapfile -t pre_proc_list < <(echo "$pre_proc_data")
81 |
82 | fetch_spec_a="${pre_proc_list[0]}";
83 | fetch_spec_b="${pre_proc_list[1]}";
84 | conv_move="${pre_proc_list[2]//$env_awk_newline_substitution/$'\n'}";
85 | victim_move="${pre_proc_list[3]//$env_awk_newline_substitution/$'\n'}";
86 | end_of_results="${pre_proc_list[4]}";
87 |
88 | # Let's export for an usage in post_fetch_processing.gawk.
89 | export conv_move
90 | export victim_move
91 |
92 | end_of_results_expected='{[end-of-results]}';
93 | # This comparison must have double quotes on the second operand. Otherwise it doesn't work.
94 | if [[ $end_of_results != "$end_of_results_expected" ]]; then
95 | echo '@' ERROR: An unexpected internal processing results end. Exit.
96 | echo
97 |
98 | # !!! EXIT !!!
99 | exit 22;
100 | fi;
101 |
102 | if [[ $env_trace_refs == 1 ]]; then
103 | echo fetch_spec_a
104 | echo "$fetch_spec_a"
105 | echo fetch_spec_b
106 | echo "$fetch_spec_b"
107 | echo conv_move
108 | echo "$conv_move"
109 | echo victim_move
110 | echo "$victim_move"
111 | fi;
112 | # exit
113 |
114 |
115 | if [[ $env_allow_async == 1 && -n "$fetch_spec_a" && -n "$fetch_spec_b" ]]; then
116 | echo;echo "> Fetch (async)"
117 |
118 | git fetch --no-tags $origin_a $fetch_spec_a > "$path_async_output/fetch_a.txt" &
119 | pid_fetch_a=$!
120 | git fetch --no-tags $origin_b $fetch_spec_b > "$path_async_output/fetch_b.txt" &
121 | pid_fetch_b=$!
122 |
123 | fetch_report_a="> Fetch $origin_a "
124 | wait $pid_fetch_a && fetch_report_a+="(async success)" || fetch_report_a+="(async failure)"
125 | fetch_report_b+="> Fetch $origin_b "
126 | wait $pid_fetch_b && fetch_report_b+="(async success)" || fetch_report_b+="(async failure)"
127 |
128 | echo $fetch_report_a
129 | echo $fetch_spec_a
130 | cat < "$path_async_output/fetch_a.txt"
131 |
132 | echo $fetch_report_b
133 | echo $fetch_spec_b
134 | cat < "$path_async_output/fetch_b.txt"
135 | else
136 | if [[ -n "$fetch_spec_a" ]]; then
137 | echo;echo "> Fetch $origin_a (sync)"
138 | echo $fetch_spec_a
139 | git fetch --no-tags $origin_a $fetch_spec_a
140 | fi;
141 | if [[ -n "$fetch_spec_b" ]]; then
142 | echo;echo "> Fetch $origin_b (sync)"
143 | echo $fetch_spec_b
144 | git fetch --no-tags $origin_b $fetch_spec_b
145 | fi;
146 | fi;
147 |
148 |
149 | track_refs_a=$(git for-each-ref --format="%(objectname) %(refname)" $track_refspecs_a)
150 | track_refs_b=$(git for-each-ref --format="%(objectname) %(refname)" $track_refspecs_b)
151 |
152 | export all_track_refs_a=$(git for-each-ref --format="%(objectname) %(refname)" $all_tracks_refspec_a)
153 | export all_track_refs_b=$(git for-each-ref --format="%(objectname) %(refname)" $all_tracks_refspec_b)
154 |
155 | # Prevents excessive processing for cleaning of excluded track refs.
156 | [[ "$all_track_refs_a" == "$track_refs_a" ]] && all_track_refs_a=;
157 | [[ "$all_track_refs_b" == "$track_refs_b" ]] && all_track_refs_b=;
158 |
159 |
160 | if [[ $env_trace_refs == 1 ]]; then
161 | echo
162 | echo track_refs_a=
163 | echo "$track_refs_a"
164 | echo track_refs_b=
165 | echo "$track_refs_b"
166 | fi;
167 | # exit
168 |
169 |
170 | proc_data=$($env_awk_edition \
171 | --file="$path_git_sync_util/gawk/post_fetch_processing.gawk" \
172 | `# --lint` \
173 | <(echo "$remote_refs_a") \
174 | <(echo "$remote_refs_b") \
175 | <(echo "$track_refs_a") \
176 | <(echo "$track_refs_b") \
177 | )
178 |
179 | mapfile -t proc_list < <(echo "$proc_data")
180 |
181 | if [[ $env_trace_refs == 1 ]]; then
182 | echo
183 | echo proc_data is
184 | echo "$proc_data"
185 | fi;
186 | # exit
187 |
188 | processing_requested="${proc_list[0]}";
189 | remove_tracking_spec="${proc_list[1]}";
190 | notify_del="${proc_list[2]//$env_awk_newline_substitution/$'\n'}";
191 |
192 | push_spec_a="${proc_list[3]}";
193 | push_spec_b="${proc_list[4]}";
194 | notify_solving="${proc_list[5]//$env_awk_newline_substitution/$'\n'}";
195 |
196 | post_fetch_spec_a="${proc_list[6]}";
197 | post_fetch_spec_b="${proc_list[7]}";
198 |
199 | end_of_results="${proc_list[8]}";
200 |
201 | if [[ $env_trace_refs == 1 ]]; then
202 | echo
203 | echo processing_requested is "$processing_requested"
204 | echo remove_tracking_spec is
205 | echo "$remove_tracking_spec"
206 | echo notify_del is
207 | echo "$notify_del"
208 | echo push_spec_a is
209 | echo "$push_spec_a"
210 | echo push_spec_b is
211 | echo "$push_spec_b"
212 | echo notify_solving is
213 | echo "$notify_solving"
214 | echo post_fetch_spec_a is
215 | echo "$post_fetch_spec_a"
216 | echo post_fetch_spec_b is
217 | echo "$post_fetch_spec_b"
218 | fi;
219 | # exit
220 |
221 | end_of_results_expected='{[end-of-results]}';
222 | # This comparison must have double quotes on the second operand. Otherwise it doesn't work.
223 | if [[ $end_of_results != "$end_of_results_expected" ]]; then
224 | echo '@' ERROR: An unexpected internal processing results end. Exit.
225 | echo
226 |
227 | # !!! EXIT !!!
228 | exit 23
229 | fi;
230 |
231 | if [[ "$processing_requested" == '1' ]]; then
232 | ((++post_fetch_processing_num))
233 | fi;
234 |
235 | mkdir -p "$path_async_output"
236 |
237 | if [[ -n "$remove_tracking_spec" ]]; then
238 | echo;echo "> Delete track branches"
239 | git branch --delete --force --remotes $remove_tracking_spec
240 | fi;
241 |
242 | if [[ -n "$notify_del" ]]; then
243 | echo;echo "> Notify Deletion"
244 |
245 | install -D /dev/null "$env_notify_del_file"
246 |
247 | echo > "$env_notify_del_file"
248 | echo "$notify_del" >> "$env_notify_del_file"
249 | fi;
250 |
251 | if [[ -n "$notify_solving" ]]; then
252 | echo;echo "> Notify Solving"
253 |
254 | install -D /dev/null "$env_notify_solving_file"
255 |
256 | echo > "$env_notify_solving_file"
257 | echo "$notify_solving" >> "$env_notify_solving_file"
258 | fi;
259 |
260 | if [[ $env_allow_async == 1 && -n "$push_spec_a" && -n "$push_spec_b" ]]; then
261 | echo;echo "> Push (async)"
262 |
263 | { git push $origin_a $push_spec_a || git_fail push $origin_a $?; } > "$path_async_output/push_a.txt" &
264 | pid_push_a=$!
265 | { git push $origin_b $push_spec_b || git_fail push $origin_b $?; } > "$path_async_output/push_b.txt" &
266 | pid_push_b=$!
267 |
268 | push_report_a="> Push $origin_a (async) "
269 | wait $pid_push_a && push_report_a+="(async success)" || push_report_a+="(async failure)"
270 | push_report_b+="> Push $origin_b (async) "
271 | wait $pid_push_b && push_report_b+="(async success)" || push_report_b+="(async failure)"
272 |
273 | echo $push_report_a
274 | echo $push_spec_a
275 | cat < "$path_async_output/push_a.txt"
276 |
277 | echo $push_report_b
278 | echo $push_spec_b
279 | cat < "$path_async_output/push_b.txt"
280 | else
281 | if [[ -n "$push_spec_a" ]]; then
282 | echo;echo "> Push $origin_a (sync)"
283 | echo $push_spec_a
284 | git push $origin_a $push_spec_a || git_fail push $origin_a $?
285 | fi;
286 | if [[ -n "$push_spec_b" ]]; then
287 | echo;echo "> Push $origin_b (sync)"
288 | echo $push_spec_b
289 | git push $origin_b $push_spec_b || git_fail push $origin_b $?
290 | fi;
291 | fi;
292 |
293 |
294 | if [[ $env_allow_async == 1 && -n "$post_fetch_spec_a" && -n "$post_fetch_spec_b" ]]; then
295 | echo;echo "> Post-fetch (async)"
296 |
297 | git fetch --no-tags $origin_a $post_fetch_spec_a > "$path_async_output/post_fetch_a.txt" &
298 | pid_post_fetch_a=$!
299 | git fetch --no-tags $origin_b $post_fetch_spec_b > "$path_async_output/post_fetch_b.txt" &
300 | pid_post_fetch_b=$!
301 |
302 | post_fetch_report_a="> Post-fetch $origin_a "
303 | wait $pid_post_fetch_a && post_fetch_report_a+="(async success)" || post_fetch_report_a+="(async failure)"
304 | post_fetch_report_b+="> Post-fetch $origin_b "
305 | wait $pid_post_fetch_b && post_fetch_report_b+="(async success)" || post_fetch_report_b+="(async failure)"
306 |
307 | echo $post_fetch_report_a
308 | echo $post_fetch_spec_a
309 | cat < "$path_async_output/post_fetch_a.txt"
310 |
311 | echo $post_fetch_report_b
312 | echo $post_fetch_spec_b
313 | cat < "$path_async_output/post_fetch_b.txt"
314 | else
315 | if [[ -n "$post_fetch_spec_a" ]]; then
316 | echo;echo "> Post-fetch $origin_a (sync)"
317 | echo $post_fetch_spec_a
318 | git fetch --no-tags $origin_a $post_fetch_spec_a
319 | fi;
320 | if [[ -n "$post_fetch_spec_b" ]]; then
321 | echo;echo "> Post-fetch $origin_b (sync)"
322 | echo $post_fetch_spec_b
323 | git fetch --no-tags $origin_b $post_fetch_spec_b
324 | fi;
325 | fi;
326 | }
327 | sync_pass
328 |
329 |
330 |
--------------------------------------------------------------------------------