[7054] Missing update to 7041
[getmangos.git] / contrib / git_id / git_id.cpp
blob05373165ea98956db87340789dae82b939d71335
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <string>
23 #include <sstream>
24 #include <assert.h>
25 #include <set>
26 #include <list>
27 #include <sstream>
28 #include "../../src/framework/Platform/CompilerDefs.h"
30 #if PLATFORM == PLATFORM_WINDOWS
31 #include <direct.h>
32 #define popen _popen
33 #define pclose _pclose
34 #define snprintf _snprintf
35 #define putenv _putenv
36 #pragma warning (disable:4996)
37 #else
38 #include <unistd.h>
39 #endif
41 // max string sizes
43 #define MAX_REMOTE 256
44 #define MAX_MSG 16384
45 #define MAX_PATH 2048
46 #define MAX_BUF 2048
47 #define MAX_CMD 2048
48 #define MAX_HASH 256
49 #define MAX_DB 256
51 // config
53 #define NUM_REMOTES 2
54 #define NUM_DATABASES 3
56 char remotes[NUM_REMOTES][MAX_REMOTE] = {
57 "git@github.com:mangos/mangos.git",
58 "git://github.com/mangos/mangos.git" // used for fetch if present
61 char remote_branch[MAX_REMOTE] = "master";
62 char rev_file[MAX_PATH] = "src/shared/revision_nr.h";
63 char sql_update_dir[MAX_PATH] = "sql/updates";
64 char new_index_file[MAX_PATH] = ".git/git_id_index";
66 char databases[NUM_DATABASES][MAX_DB] = {
67 "characters",
68 "mangos",
69 "realmd"
72 char db_version_table[NUM_DATABASES][MAX_DB] = {
73 "character_db_version",
74 "db_version",
75 "realmd_db_version",
78 char db_sql_file[NUM_DATABASES][MAX_PATH] = {
79 "sql/characters.sql",
80 "sql/mangos.sql",
81 "sql/realmd.sql"
84 bool allow_replace = false;
85 bool local = false;
86 bool do_fetch = false;
87 bool do_sql = false;
88 bool use_new_index = true;
90 // aux
92 char origins[NUM_REMOTES][MAX_REMOTE];
93 int rev;
94 int last_sql_rev[NUM_DATABASES] = {0,0,0};
95 int last_sql_nr[NUM_DATABASES] = {0,0,0};
97 char head_message[MAX_MSG];
98 char path_prefix[MAX_PATH] = "";
99 char base_path[MAX_PATH];
100 char buffer[MAX_BUF];
101 char cmd[MAX_CMD];
102 char origin_hash[MAX_HASH];
103 char last_sql_update[NUM_DATABASES][MAX_PATH];
104 char old_index_cmd[MAX_CMD];
105 char new_index_cmd[MAX_CMD];
107 std::set<std::string> new_sql_updates;
109 FILE *cmd_pipe;
111 bool find_path()
113 printf("+ finding path\n");
114 char *ptr;
115 char cur_path[MAX_PATH];
116 getcwd(cur_path, MAX_PATH);
117 int len = strlen(cur_path);
118 strncpy(base_path, cur_path, len+1);
120 if(cur_path[len-1] == '/' || cur_path[len-1] == '\\')
122 // we're in root, don't bother
123 return false;
126 // don't count the root
127 int count_fwd = 0, count_back = 0;
128 for(ptr = cur_path-1; ptr = strchr(ptr+1, '/'); count_fwd++);
129 for(ptr = cur_path-1; ptr = strchr(ptr+1, '\\'); count_back++);
130 int count = std::max(count_fwd, count_back);
132 char path[MAX_PATH];
133 for(int i = 0; i < count; i++)
135 snprintf(path, MAX_PATH, "%s.git", path_prefix);
136 if(0 == chdir(path))
138 chdir(cur_path);
139 return true;
141 strncat(path_prefix, "../", MAX_PATH);
143 ptr = strrchr(base_path, '\\');
144 if(ptr) *ptr = '\0';
145 else
147 ptr = strrchr(base_path, '/');
148 if(ptr) *ptr = '\0';
152 return false;
155 bool find_origin()
157 printf("+ finding origin\n");
158 if( (cmd_pipe = popen( "git remote -v", "r" )) == NULL )
159 return false;
161 bool ret = false;
162 while(fgets(buffer, MAX_BUF, cmd_pipe))
164 char name[256], remote[MAX_REMOTE];
165 sscanf(buffer, "%s %s", name, remote);
166 for(int i = 0; i < NUM_REMOTES; i++)
168 if(strcmp(remote, remotes[i]) == 0)
170 strncpy(origins[i], name, MAX_REMOTE);
171 ret = true;
175 pclose(cmd_pipe);
176 return ret;
179 bool fetch_origin()
181 printf("+ fetching origin\n");
182 // use the public clone url if present because the private may require a password
183 snprintf(cmd, MAX_CMD, "git fetch %s %s", (origins[1][0] ? origins[1] : origins[0]), remote_branch);
184 int ret = system(cmd);
185 return true;
188 bool check_fwd()
190 printf("+ checking fast forward\n");
191 snprintf(cmd, MAX_CMD, "git log -n 1 --pretty=\"format:%%H\" %s/%s", (origins[1][0] ? origins[1] : origins[0]), remote_branch);
192 if( (cmd_pipe = popen( cmd, "r" )) == NULL )
193 return false;
195 if(!fgets(buffer, MAX_BUF, cmd_pipe)) return false;
196 strncpy(origin_hash, buffer, MAX_HASH);
197 pclose(cmd_pipe);
199 if( (cmd_pipe = popen( "git log --pretty=\"format:%H\"", "r" )) == NULL )
200 return false;
202 bool found = false;
203 while(fgets(buffer, MAX_BUF, cmd_pipe))
205 buffer[strlen(buffer) - 1] = '\0';
206 if(strncmp(origin_hash, buffer, MAX_BUF) == 0)
208 found = true;
209 break;
212 pclose(cmd_pipe);
214 if(!found)
216 // with fetch you still get the latest rev, you just rebase afterwards and push
217 // without it you may not get the right rev
218 if(do_fetch) printf("WARNING: non-fastforward, use rebase!\n");
219 else { printf("ERROR: non-fastforward, use rebase!\n"); return false; }
221 return true;
224 int get_rev(const char *from_msg)
226 // accept only the rev number format, not the sql update format
227 char nr_str[256];
228 if(sscanf(from_msg, "[%[0123456789]]", nr_str) != 1) return 0;
229 if(from_msg[strlen(nr_str)+1] != ']') return 0;
230 return atoi(nr_str);
233 bool find_rev()
235 printf("+ finding next revision number\n");
236 // find the highest rev number on either of the remotes
237 for(int i = 0; i < NUM_REMOTES; i++)
239 if(!local && !origins[i][0]) continue;
241 if(local) snprintf(cmd, MAX_CMD, "git log HEAD --pretty=\"format:%%s\"");
242 else sprintf(cmd, "git log %s/%s --pretty=\"format:%%s\"", origins[i], remote_branch);
243 if( (cmd_pipe = popen( cmd, "r" )) == NULL )
244 continue;
246 int nr;
247 while(fgets(buffer, MAX_BUF, cmd_pipe))
249 nr = get_rev(buffer);
250 if(nr >= rev)
251 rev = nr+1;
253 pclose(cmd_pipe);
256 if(rev > 0) printf("Found [%d].\n", rev);
258 return rev > 0;
261 std::string generateHeader(char const* rev_str)
263 std::ostringstream newData;
264 newData << "#ifndef __REVISION_NR_H__" << std::endl;
265 newData << "#define __REVISION_NR_H__" << std::endl;
266 newData << " #define REVISION_NR \"" << rev_str << "\"" << std::endl;
267 newData << "#endif // __REVISION_NR_H__" << std::endl;
268 return newData.str();
271 void system_switch_index(const char *cmd)
273 // do the command for the original index and then for the new index
274 // both need to be updated with the changes before commit
275 // but the new index will contains only the desired changes
276 // while the old may contain others
277 system(cmd);
278 if(!use_new_index) return;
279 if(putenv(new_index_cmd) != 0) return;
280 system(cmd);
281 if(putenv(old_index_cmd) != 0) return;
284 bool write_rev()
286 printf("+ writing revision_nr.h\n");
287 char rev_str[256];
288 sprintf(rev_str, "%d", rev);
289 std::string header = generateHeader(rev_str);
291 char prefixed_file[MAX_PATH];
292 snprintf(prefixed_file, MAX_PATH, "%s%s", path_prefix, rev_file);
294 if(FILE* OutputFile = fopen(prefixed_file, "wb"))
296 fprintf(OutputFile,"%s", header.c_str());
297 fclose(OutputFile);
299 // add the file to both indices, to be committed later
300 snprintf(cmd, MAX_CMD, "git add %s", prefixed_file);
301 system_switch_index(cmd);
303 return true;
306 return false;
309 bool find_head_msg()
311 printf("+ finding last message on HEAD\n");
312 if( (cmd_pipe = popen( "git log -n 1 --pretty=\"format:%s%n%n%b\"", "r" )) == NULL )
313 return false;
315 int poz = 0;
316 while(poz < 16384-1 && EOF != (head_message[poz++] = fgetc(cmd_pipe)));
317 head_message[poz-1] = '\0';
319 pclose(cmd_pipe);
321 if(int head_rev = get_rev(head_message))
323 if(!allow_replace)
325 printf("Last commit on HEAD is [%d]. Use -r to replace it with [%d].\n", head_rev, rev);
326 return false;
329 // skip the rev number in the commit
330 char *p = strchr(head_message, ']'), *q = head_message;
331 assert(p && *(p+1));
332 p+=2;
333 while(*p) *q = *p, p++, q++;
334 *q = 0;
335 return true;
338 return true;
341 bool amend_commit()
343 printf("+ amending last commit\n");
345 // commit the contents of the (new) index
346 if(use_new_index && putenv(new_index_cmd) != 0) return false;
347 snprintf(cmd, MAX_CMD, "git commit --amend -F-");
348 if( (cmd_pipe = popen( cmd, "w" )) == NULL )
349 return false;
351 fprintf(cmd_pipe, "[%d] %s", rev, head_message);
352 pclose(cmd_pipe);
353 if(use_new_index && putenv(old_index_cmd) != 0) return false;
355 return true;
358 struct sql_update_info
360 int rev;
361 int nr;
362 int db_idx;
363 char db[MAX_BUF];
364 char table[MAX_BUF];
365 bool has_table;
368 bool get_sql_update_info(const char *buffer, sql_update_info &info)
370 info.table[0] = '\0';
371 int dummy[3];
372 if(sscanf(buffer, "%d_%d_%d", &dummy[0], &dummy[1], &dummy[2]) == 3)
373 return false;
375 if(sscanf(buffer, "%d_%d_%[^_]_%[^.].sql", &info.rev, &info.nr, info.db, info.table) != 4 &&
376 sscanf(buffer, "%d_%d_%[^.].sql", &info.rev, &info.nr, info.db) != 3)
378 info.rev = 0; // this may be set by the first scans, even if they fail
379 if(sscanf(buffer, "%d_%[^_]_%[^.].sql", &info.nr, info.db, info.table) != 3 &&
380 sscanf(buffer, "%d_%[^.].sql", &info.nr, info.db) != 2)
381 return false;
384 for(info.db_idx = 0; info.db_idx < NUM_DATABASES; info.db_idx++)
385 if(strncmp(info.db, databases[info.db_idx], MAX_DB) == 0) break;
386 info.has_table = (info.table[0] != '\0');
387 return true;
390 bool find_sql_updates()
392 printf("+ finding new sql updates on HEAD\n");
393 // add all updates from HEAD
394 snprintf(cmd, MAX_CMD, "git show HEAD:%s", sql_update_dir);
395 if( (cmd_pipe = popen( cmd, "r" )) == NULL )
396 return false;
398 // skip first two lines
399 if(!fgets(buffer, MAX_BUF, cmd_pipe)) { pclose(cmd_pipe); return false; }
400 if(!fgets(buffer, MAX_BUF, cmd_pipe)) { pclose(cmd_pipe); return false; }
402 sql_update_info info;
404 while(fgets(buffer, MAX_BUF, cmd_pipe))
406 buffer[strlen(buffer) - 1] = '\0';
407 if(!get_sql_update_info(buffer, info)) continue;
409 if(info.db_idx == NUM_DATABASES)
411 if(info.rev > 0) printf("WARNING: incorrect database name for sql update %s\n", buffer);
412 continue;
415 new_sql_updates.insert(buffer);
418 pclose(cmd_pipe);
420 // remove updates from the last commit also found on origin
421 snprintf(cmd, MAX_CMD, "git show %s:%s", origin_hash, sql_update_dir);
422 if( (cmd_pipe = popen( cmd, "r" )) == NULL )
423 return false;
425 // skip first two lines
426 if(!fgets(buffer, MAX_BUF, cmd_pipe)) { pclose(cmd_pipe); return false; }
427 if(!fgets(buffer, MAX_BUF, cmd_pipe)) { pclose(cmd_pipe); return false; }
429 while(fgets(buffer, MAX_BUF, cmd_pipe))
431 buffer[strlen(buffer) - 1] = '\0';
432 if(!get_sql_update_info(buffer, info)) continue;
434 // find the old update with the highest rev for each database
435 // (will be the required version for the new update)
436 std::set<std::string>::iterator itr = new_sql_updates.find(buffer);
437 if(itr != new_sql_updates.end() )
439 if(info.rev > 0 && (info.rev > last_sql_rev[info.db_idx] ||
440 (info.rev == last_sql_rev[info.db_idx] && info.nr > last_sql_nr[info.db_idx])))
442 last_sql_rev[info.db_idx] = info.rev;
443 last_sql_nr[info.db_idx] = info.nr;
444 sscanf(buffer, "%[^.]", last_sql_update[info.db_idx]);
446 new_sql_updates.erase(itr);
450 pclose(cmd_pipe);
452 if(!new_sql_updates.empty())
454 for(std::set<std::string>::iterator itr = new_sql_updates.begin(); itr != new_sql_updates.end(); ++itr)
455 printf("%s\n", itr->c_str());
457 else
458 printf("WARNING: no new sql updates found.\n");
460 return true;
463 bool copy_file(const char *src_file, const char *dst_file)
465 FILE * fin = fopen( src_file, "rb" );
466 if(!fin) return false;
467 FILE * fout = fopen( dst_file, "wb" );
468 if(!fout) { fclose(fin); return false; }
470 for(char c = getc(fin); !feof(fin); putc(c, fout), c = getc(fin));
472 fclose(fin);
473 fclose(fout);
474 return true;
477 bool convert_sql_updates()
479 if(new_sql_updates.empty()) return true;
481 printf("+ converting sql updates\n");
483 // rename the sql update files and add the required update statement
484 for(std::set<std::string>::iterator itr = new_sql_updates.begin(); itr != new_sql_updates.end(); ++itr)
486 sql_update_info info;
487 if(!get_sql_update_info(itr->c_str(), info)) return false;
488 if(info.db_idx == NUM_DATABASES) return false;
490 // generating the new name should work for updates with or without a rev
491 char src_file[MAX_PATH], new_name[MAX_PATH], dst_file[MAX_PATH];
492 snprintf(src_file, MAX_PATH, "%s%s/%s", path_prefix, sql_update_dir, itr->c_str());
493 snprintf(new_name, MAX_PATH, "%d_%0*d_%s%s%s", rev, 2, info.nr, info.db, info.has_table ? "_" : "", info.table);
494 snprintf(dst_file, MAX_PATH, "%s%s/%s.sql", path_prefix, sql_update_dir, new_name);
496 FILE * fin = fopen( src_file, "r" );
497 if(!fin) return false;
498 FILE * fout = fopen( dst_file, "w" );
499 if(!fout) { fclose(fin); return false; }
501 // add the update requirements
502 fprintf(fout, "ALTER TABLE %s CHANGE COLUMN required_%s required_%s bit;\n\n",
503 db_version_table[info.db_idx], last_sql_update[info.db_idx], new_name);
505 // skip the first one or two lines from the input
506 // if it already contains update requirements
507 if(fgets(buffer, MAX_BUF, fin))
509 char dummy[MAX_BUF];
510 if(sscanf(buffer, "ALTER TABLE %s CHANGE COLUMN required_%s required_%s bit", dummy, dummy, dummy) == 3)
512 if(fgets(buffer, MAX_BUF, fin) && buffer[0] != '\n')
513 fputs(buffer, fout);
515 else
516 fputs(buffer, fout);
519 // copy the rest of the file
520 char c;
521 while( (c = getc(fin)) != EOF )
522 putc(c, fout);
524 fclose(fin);
525 fclose(fout);
527 // rename the file in git
528 snprintf(cmd, MAX_CMD, "git add %s", dst_file);
529 system_switch_index(cmd);
530 snprintf(cmd, MAX_CMD, "git rm --quiet %s", src_file);
531 system_switch_index(cmd);
533 // update the last sql update for the current database
534 strncpy(last_sql_update[info.db_idx], new_name, MAX_PATH);
537 return true;
540 bool generate_sql_makefile()
542 if(new_sql_updates.empty()) return true;
544 // find all files in the update dir
545 snprintf(cmd, MAX_CMD, "git show HEAD:%s", sql_update_dir);
546 if( (cmd_pipe = popen( cmd, "r" )) == NULL )
547 return false;
549 // skip first two lines
550 if(!fgets(buffer, MAX_BUF, cmd_pipe)) { pclose(cmd_pipe); return false; }
551 if(!fgets(buffer, MAX_BUF, cmd_pipe)) { pclose(cmd_pipe); return false; }
553 char newname[MAX_PATH];
554 std::set<std::string> file_list;
555 sql_update_info info;
557 while(fgets(buffer, MAX_BUF, cmd_pipe))
559 buffer[strlen(buffer) - 1] = '\0';
560 if(buffer[strlen(buffer) - 1] != '/' &&
561 strncmp(buffer, "Makefile.am", MAX_BUF) != 0)
563 if(new_sql_updates.find(buffer) != new_sql_updates.end())
565 if(!get_sql_update_info(buffer, info)) return false;
566 snprintf(newname, MAX_PATH, "%d_%0*d_%s%s%s.sql", rev, 2, info.nr, info.db, info.has_table ? "_" : "", info.table);
567 file_list.insert(newname);
569 else
570 file_list.insert(buffer);
574 pclose(cmd_pipe);
576 // write the makefile
577 char file_name[MAX_PATH];
578 snprintf(file_name, MAX_PATH, "%s%s/Makefile.am", path_prefix, sql_update_dir);
579 FILE *fout = fopen(file_name, "w");
580 if(!fout) { pclose(cmd_pipe); return false; }
582 fprintf(fout,
583 "# Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>\n"
584 "#\n"
585 "# This program is free software; you can redistribute it and/or modify\n"
586 "# it under the terms of the GNU General Public License as published by\n"
587 "# the Free Software Foundation; either version 2 of the License, or\n"
588 "# (at your option) any later version.\n"
589 "#\n"
590 "# This program is distributed in the hope that it will be useful,\n"
591 "# but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
592 "# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
593 "# GNU General Public License for more details.\n"
594 "#\n"
595 "# You should have received a copy of the GNU General Public License\n"
596 "# along with this program; if not, write to the Free Software\n"
597 "# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
598 "\n"
599 "## Process this file with automake to produce Makefile.in\n"
600 "\n"
601 "## Sub-directories to parse\n"
602 "\n"
603 "## Change installation location\n"
604 "# datadir = mangos/%s\n"
605 "pkgdatadir = $(datadir)/mangos/%s\n"
606 "\n"
607 "## Files to be installed\n"
608 "# Install basic SQL files to datadir\n"
609 "pkgdata_DATA = \\\n",
610 sql_update_dir, sql_update_dir
613 for(std::set<std::string>::iterator itr = file_list.begin(), next; itr != file_list.end(); ++itr)
615 next = itr; ++next;
616 fprintf(fout, "\t%s%s\n", itr->c_str(), next == file_list.end() ? "" : " \\");
619 fprintf(fout,
620 "\n## Additional files to include when running 'make dist'\n"
621 "# SQL update files, to upgrade database schema from older revisions\n"
622 "EXTRA_DIST = \\\n"
625 for(std::set<std::string>::iterator itr = file_list.begin(), next; itr != file_list.end(); ++itr)
627 next = itr; ++next;
628 fprintf(fout, "\t%s%s\n", itr->c_str(), next == file_list.end() ? "" : " \\");
631 fclose(fout);
633 snprintf(cmd, MAX_CMD, "git add %s%s/Makefile.am", path_prefix, sql_update_dir);
634 system_switch_index(cmd);
636 return true;
639 bool change_sql_database()
641 if(new_sql_updates.empty()) return true;
642 printf("+ changing database sql files\n");
644 // rename the database files, copy their contents back
645 // and change the required update line
646 for(int i = 0; i < NUM_DATABASES; i++)
648 if(last_sql_update[i][0] == '\0') continue;
650 char old_file[MAX_PATH], tmp_file[MAX_PATH], dummy[MAX_BUF];
652 snprintf(old_file, MAX_PATH, "%s%s", path_prefix, db_sql_file[i]);
653 snprintf(tmp_file, MAX_PATH, "%s%stmp", path_prefix, db_sql_file[i]);
655 rename(old_file, tmp_file);
657 FILE *fin = fopen( tmp_file, "r" );
658 if(!fin) return false;
659 FILE *fout = fopen( old_file, "w" );
660 if(!fout) return false;
662 snprintf(dummy, MAX_CMD, "CREATE TABLE `%s` (\n", db_version_table[i]);
663 while(fgets(buffer, MAX_BUF, fin))
665 fputs(buffer, fout);
666 if(strncmp(buffer, dummy, MAX_BUF) == 0)
667 break;
670 while(1)
672 if(!fgets(buffer, MAX_BUF, fin)) return false;
673 if(sscanf(buffer, " `required_%s`", dummy) == 1) break;
674 fputs(buffer, fout);
677 fprintf(fout, " `required_%s` bit(1) default NULL\n", last_sql_update[i]);
678 while(fgets(buffer, MAX_BUF, fin))
679 fputs(buffer, fout);
681 fclose(fin);
682 fclose(fout);
683 remove(tmp_file);
685 snprintf(cmd, MAX_CMD, "git add %s", old_file);
686 system_switch_index(cmd);
688 return true;
691 bool change_sql_history()
693 snprintf(cmd, MAX_CMD, "git log HEAD --pretty=\"format:%%H\"");
694 if( (cmd_pipe = popen( cmd, "r" )) == NULL )
695 return false;
697 std::list<std::string> hashes;
698 while(fgets(buffer, MAX_BUF, cmd_pipe))
700 buffer[strlen(buffer) - 1] = '\0';
701 if(strncmp(origin_hash, buffer, MAX_HASH) == 0)
702 break;
704 hashes.push_back(buffer);
706 pclose(cmd_pipe);
707 if(hashes.empty()) return false; // must have at least one commit
708 if(hashes.size() < 2) return true; // only one commit, ok but nothing to do
710 snprintf(cmd, MAX_CMD, "git reset --hard %s", origin_hash);
711 system(cmd);
713 for(std::list<std::string>::reverse_iterator next = hashes.rbegin(), itr = next++; next != hashes.rend(); ++itr, ++next)
715 // stage the changes from the orignal commit
716 snprintf(cmd, MAX_CMD, "git cherry-pick -n %s", itr->c_str());
717 system(cmd);
719 // remove changed and deleted files
720 snprintf(cmd, MAX_CMD, "git checkout HEAD %s%s", path_prefix, sql_update_dir);
721 system(cmd);
723 // remove the newly added files
724 snprintf(cmd, MAX_CMD, "git diff --cached --diff-filter=A --name-only %s%s", path_prefix, sql_update_dir);
725 if( (cmd_pipe = popen( cmd, "r" )) == NULL )
726 return false;
728 while(fgets(buffer, MAX_BUF, cmd_pipe))
730 buffer[strlen(buffer) - 1] = '\0';
731 snprintf(cmd, MAX_CMD, "git rm -f --quiet %s%s", path_prefix, buffer);
732 system(cmd);
735 pclose(cmd_pipe);
737 // make a commit with the same author and message as the original one
739 snprintf(cmd, MAX_CMD, "git commit -C %s", itr->c_str());
740 system(cmd);
743 snprintf(cmd, MAX_CMD, "git cherry-pick %s", hashes.begin()->c_str());
744 system(cmd);
746 return true;
749 bool prepare_new_index()
751 if(!use_new_index) return true;
753 // only use a new index if there are staged changes that should be preserved
754 if( (cmd_pipe = popen( "git diff --cached", "r" )) == NULL )
755 return false;
757 if(!fgets(buffer, MAX_BUF, cmd_pipe))
759 use_new_index = false;
760 pclose(cmd_pipe);
761 return true;
764 pclose(cmd_pipe);
766 printf("+ preparing new index\n");
768 // copy the existing index file to a new one
769 char src_file[MAX_PATH], dst_file[MAX_PATH];
771 char *old_index = getenv("GIT_INDEX_FILE");
772 if(old_index) strncpy(src_file, old_index, MAX_PATH);
773 else snprintf(src_file, MAX_PATH, "%s.git/index", path_prefix);
774 snprintf(dst_file, MAX_PATH, "%s%s", path_prefix, new_index_file);
776 if(!copy_file(src_file, dst_file)) return false;
778 // doesn't seem to work with path_prefix
779 snprintf(new_index_cmd, MAX_CMD, "GIT_INDEX_FILE=%s/%s", base_path, new_index_file);
780 if(putenv(new_index_cmd) != 0) return false;
782 // clear the new index
783 system("git reset -q --mixed HEAD");
785 // revert to old index
786 snprintf(old_index_cmd, MAX_CMD, "GIT_INDEX_FILE=");
787 if(putenv(old_index_cmd) != 0) return false;
788 return true;
791 bool cleanup_new_index()
793 if(!use_new_index) return true;
794 printf("+ cleaning up the new index\n");
795 char idx_file[MAX_PATH];
796 snprintf(idx_file, MAX_PATH, "%s%s", path_prefix, new_index_file);
797 remove(idx_file);
798 return true;
801 #define DO(cmd) if(!cmd) { printf("FAILED\n"); return 1; }
803 int main(int argc, char *argv[])
805 for(int i = 1; i < argc; i++)
807 if(argv[i] == NULL) continue;
808 if(strncmp(argv[i], "-r", 2) == 0 || strncmp(argv[i], "--replace", 9) == 0)
809 allow_replace = true;
810 else if(strncmp(argv[i], "-l", 2) == 0 || strncmp(argv[i], "--local", 7) == 0)
811 local = true;
812 else if(strncmp(argv[i], "-f", 2) == 0 || strncmp(argv[i], "--fetch", 7) == 0)
813 do_fetch = true;
814 else if(strncmp(argv[i], "-s", 2) == 0 || strncmp(argv[i], "--sql", 5) == 0)
815 do_sql = true;
816 else if(strncmp(argv[i], "--branch=", 9) == 0)
817 snprintf(remote_branch, MAX_REMOTE, "%s", argv[i] + 9);
818 else if(strncmp(argv[i], "-h", 2) == 0 || strncmp(argv[i], "--help", 6) == 0)
820 printf("Usage: git_id [OPTION]\n");
821 printf("Generates a new rev number and updates revision_nr.h and the commit message.\n");
822 printf("Should be used just before push.\n");
823 printf(" -h, --help show the usage\n");
824 printf(" -r, --replace replace the rev number if it was already applied\n");
825 printf(" to the last commit\n");
826 printf(" -l, --local search for the highest rev number on HEAD\n");
827 printf(" -f, --fetch fetch from origin before searching for the new rev\n");
828 printf(" -s, --sql search for new sql updates and do all of the changes\n");
829 printf(" for the new rev\n");
830 printf(" --branch=BRANCH specify which remote branch to use\n");
831 return 0;
835 DO( find_path() );
836 if(!local)
838 DO( find_origin() );
839 if(do_fetch)
840 DO( fetch_origin() );
841 DO( check_fwd() );
843 DO( find_rev() );
844 DO( find_head_msg() );
845 if(do_sql)
846 DO( find_sql_updates() );
847 DO( prepare_new_index() );
848 DO( write_rev() );
849 if(do_sql)
851 DO( convert_sql_updates() );
852 DO( generate_sql_makefile() );
853 DO( change_sql_database() );
855 DO( amend_commit() );
856 DO( cleanup_new_index() );
857 //if(do_sql)
858 // DO( change_sql_history() );
860 return 0;