Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F625757
Git.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
21 KB
Referenced Files
None
Subscribers
None
Git.cpp
View Options
#include
"Git.h"
#include
<QDateTime>
#include
<QDebug>
#include
<QDir>
#include
<QDirIterator>
#include
<QProcess>
#include
<QTimer>
#include
<set>
#include
"joinpath.h"
#include
"misc.h"
#include
"LibGit2.h"
#include
"GitObjectManager.h"
#define DEBUGLOG 0
struct
Git
::
Private
{
QString
git_command
;
QByteArray
result
;
int
process_exit_code
=
0
;
QString
working_repo_dir
;
QString
error_message
;
callback_t
callback_func
=
nullptr
;
void
*
callback_cookie
=
nullptr
;
};
Git
::
Git
(
const
Context
&
cx
,
QString
const
&
repodir
)
{
pv
=
new
Private
();
setGitCommand
(
cx
.
git_command
);
setWorkingRepositoryDir
(
repodir
);
}
Git
::~
Git
()
{
delete
pv
;
}
void
Git
::
setLogCallback
(
callback_t
func
,
void
*
cookie
)
{
pv
->
callback_func
=
func
;
pv
->
callback_cookie
=
cookie
;
}
void
Git
::
setWorkingRepositoryDir
(
QString
const
&
repo
)
{
pv
->
working_repo_dir
=
repo
;
}
QString
const
&
Git
::
workingRepositoryDir
()
const
{
return
pv
->
working_repo_dir
;
}
bool
Git
::
isValidID
(
const
QString
&
id
)
{
int
zero
=
0
;
int
n
=
id
.
size
();
if
(
n
>
2
&&
n
<=
GIT_ID_LENGTH
)
{
ushort
const
*
p
=
id
.
utf16
();
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
uchar
c
=
p
[
i
];
if
(
c
==
'0'
)
{
zero
++
;
}
else
if
((
c
>=
'0'
&&
c
<=
'9'
)
||
(
c
>=
'a'
&&
c
<=
'f'
))
{
// ok
}
else
{
return
false
;
}
}
if
(
zero
==
GIT_ID_LENGTH
)
{
return
false
;
}
return
true
;
// ok
}
return
false
;
}
QByteArray
Git
::
result
()
const
{
return
pv
->
result
;
}
void
Git
::
setGitCommand
(
QString
const
&
path
)
{
pv
->
git_command
=
path
;
}
QString
Git
::
gitCommand
()
const
{
Q_ASSERT
(
pv
);
return
pv
->
git_command
;
}
void
Git
::
clearResult
()
{
pv
->
result
.
clear
();
pv
->
process_exit_code
=
0
;
pv
->
error_message
=
QString
();
}
QString
Git
::
resultText
()
const
{
return
QString
::
fromUtf8
(
result
());
}
int
Git
::
getProcessExitCode
()
const
{
return
pv
->
process_exit_code
;
}
bool
Git
::
chdirexec
(
std
::
function
<
bool
()
>
fn
)
{
bool
ok
=
false
;
QDir
cwd
=
QDir
::
current
();
QString
dir
=
workingRepositoryDir
();
if
(
isValidWorkingCopy
(
dir
)
&&
QDir
::
setCurrent
(
dir
))
{
ok
=
fn
();
QDir
::
setCurrent
(
cwd
.
path
());
}
return
ok
;
}
bool
Git
::
git
(
const
QString
&
arg
,
bool
chdir
,
bool
errout
)
{
QFileInfo
info
(
gitCommand
());
if
(
!
info
.
isExecutable
())
{
qDebug
()
<<
"Invalid git command: "
<<
gitCommand
();
return
false
;
}
#if DEBUGLOG
qDebug
()
<<
"exec: git "
<<
arg
;
QTime
timer
;
timer
.
start
();
#endif
clearResult
();
auto
DoIt
=
[
&
](){
QString
cmd
=
QString
(
"
\"
%1
\"
"
).
arg
(
gitCommand
());
cmd
+=
arg
;
if
(
pv
->
callback_func
)
{
QString
prompt
=
"> "
;
QByteArray
ba
=
(
prompt
+
arg
).
toUtf8
();
ba
.
push_back
(
'\n'
);
pv
->
callback_func
(
pv
->
callback_cookie
,
ba
.
data
(),
(
int
)
ba
.
size
());
}
// qDebug() << cmd;
#if 1
QProcess
proc
;
proc
.
start
(
cmd
);
proc
.
waitForStarted
();
proc
.
closeWriteChannel
();
proc
.
setReadChannel
(
errout
?
QProcess
::
StandardError
:
QProcess
::
StandardOutput
);
while
(
1
)
{
QProcess
::
ProcessState
s
=
proc
.
state
();
if
(
proc
.
waitForReadyRead
(
1
))
{
while
(
1
)
{
char
tmp
[
1024
];
int
len
=
(
int
)
proc
.
read
(
tmp
,
sizeof
(
tmp
));
if
(
len
<
1
)
break
;
pv
->
result
.
append
(
tmp
,
len
);
if
(
pv
->
callback_func
)
{
bool
ok
=
pv
->
callback_func
(
pv
->
callback_cookie
,
tmp
,
len
);
if
(
!
ok
)
{
proc
.
terminate
();
pv
->
error_message
=
"Interrupted by user"
;
qDebug
()
<<
pv
->
error_message
;
pv
->
process_exit_code
=
-1
;
return
false
;
}
}
}
}
else
if
(
s
==
QProcess
::
NotRunning
)
{
break
;
}
}
pv
->
process_exit_code
=
proc
.
exitCode
();
#else
pv
->
process_exit_code
=
misc
::
runCommand
(
cmd
,
&
pv
->
result
);
#endif
if
(
pv
->
process_exit_code
!=
0
)
{
pv
->
error_message
=
QString
::
fromUtf8
(
proc
.
readAllStandardError
());
#if 1
//DEBUGLOG
qDebug
()
<<
QString
(
"Process exit code: %1"
).
arg
(
getProcessExitCode
());
qDebug
()
<<
pv
->
error_message
;
#endif
}
return
pv
->
process_exit_code
==
0
;
};
bool
ok
=
false
;
if
(
chdir
)
{
ok
=
chdirexec
(
DoIt
);
}
else
{
ok
=
DoIt
();
}
#if DEBUGLOG
qDebug
()
<<
timer
.
elapsed
()
<<
"ms"
;
#endif
return
ok
;
}
bool
Git
::
git
(
const
QString
&
arg
,
bool
errout
,
void
(
*
callback
)(
void
*
),
void
*
cookie
)
{
return
git
(
arg
,
true
,
errout
);
}
QString
Git
::
errorMessage
()
const
{
return
pv
->
error_message
;
}
GitPtr
Git
::
dup
()
const
{
Git
*
p
=
new
Git
();
p
->
pv
=
new
Private
();
p
->
pv
->
git_command
=
pv
->
git_command
;
p
->
pv
->
working_repo_dir
=
pv
->
working_repo_dir
;
p
->
pv
->
callback_func
=
pv
->
callback_func
;
p
->
pv
->
callback_cookie
=
pv
->
callback_cookie
;
return
GitPtr
(
p
);
}
bool
Git
::
isValidWorkingCopy
(
QString
const
&
dir
)
{
if
(
dir
.
isEmpty
())
return
false
;
return
QDir
(
dir
/
".git"
).
exists
();
}
bool
Git
::
isValidWorkingCopy
()
{
return
isValidWorkingCopy
(
workingRepositoryDir
());
}
QString
Git
::
version
()
{
git
(
"--version"
,
false
);
return
resultText
().
trimmed
();
}
QString
Git
::
rev_parse
(
QString
const
&
name
)
{
QString
cmd
=
"rev-parse %1"
;
cmd
=
cmd
.
arg
(
name
);
git
(
cmd
);
return
resultText
().
trimmed
();
}
QList
<
Git
::
Tag
>
Git
::
tags
()
{
QList
<
Git
::
Tag
>
list
;
git
(
"tag --format %(objectname)#%(refname)"
);
QStringList
lines
=
misc
::
splitLines
(
resultText
());
for
(
QString
const
&
line
:
lines
)
{
Tag
tag
;
if
(
line
.
isEmpty
())
continue
;
int
i
=
line
.
indexOf
(
'#'
);
if
(
i
==
GIT_ID_LENGTH
)
{
tag
.
id
=
line
.
mid
(
0
,
i
);
auto
MidCmp
=
[](
QString
const
&
line
,
int
i
,
char
const
*
ptr
){
ushort
const
*
p
=
line
.
utf16
();
for
(
int
j
=
0
;
ptr
[
j
];
j
++
)
{
if
(
p
[
i
+
j
]
!=
ptr
[
j
])
return
false
;
}
};
if
(
MidCmp
(
line
,
i
,
"#refs/tags/"
))
{
tag
.
name
=
line
.
mid
(
i
+
11
).
trimmed
();
if
(
tag
.
name
.
isEmpty
())
continue
;
list
.
push_back
(
tag
);
}
}
}
return
list
;
}
void
Git
::
tag
(
const
QString
&
name
,
const
QString
&
id
)
{
QString
cmd
=
"tag
\"
%1
\"
%2"
;
cmd
=
cmd
.
arg
(
name
).
arg
(
id
);
git
(
cmd
);
}
void
Git
::
delete_tag
(
const
QString
&
name
,
bool
remote
)
{
QString
cmd
=
"tag --delete
\"
%1
\"
"
;
cmd
=
cmd
.
arg
(
name
);
git
(
cmd
);
if
(
remote
)
{
QString
cmd
=
"push --delete origin
\"
%1
\"
"
;
cmd
=
cmd
.
arg
(
name
);
git
(
cmd
);
}
}
#if USE_LIBGIT2
QString
Git
::
diffHeadToWorkingDir_
()
{
std
::
string
dir
=
workingRepositoryDir
().
toStdString
();
LibGit2
::
Repository
r
=
LibGit2
::
openRepository
(
dir
);
std
::
string
s
=
r
.
diff_head_to_workdir
();
return
QString
::
fromStdString
(
s
);
}
QString
Git
::
diff_
(
QString
const
&
old_id
,
QString
const
&
new_id
)
{
if
(
new_id
.
isEmpty
())
{
return
diffHeadToWorkingDir_
();
}
else
{
std
::
string
dir
=
workingRepositoryDir
().
toStdString
();
LibGit2
::
Repository
r
=
LibGit2
::
openRepository
(
dir
);
std
::
string
s
=
r
.
diff2
(
old_id
.
toStdString
(),
new_id
.
toStdString
());
return
QString
::
fromStdString
(
s
);
}
}
#endif
QString
Git
::
diff
(
QString
const
&
old_id
,
QString
const
&
new_id
)
{
#if 1
QString
cmd
=
"diff --full-index -a %1 %2"
;
cmd
=
cmd
.
arg
(
old_id
).
arg
(
new_id
);
git
(
cmd
);
return
resultText
();
#else
return
diff_
(
old_id
,
new_id
);
#endif
}
QList
<
Git
::
DiffRaw
>
Git
::
diff_raw
(
QString
const
&
old_id
,
QString
const
&
new_id
)
{
QList
<
DiffRaw
>
list
;
QString
cmd
=
"diff --raw --abbrev=%1 %2 %3"
;
cmd
=
cmd
.
arg
(
GIT_ID_LENGTH
).
arg
(
old_id
).
arg
(
new_id
);
git
(
cmd
);
QString
text
=
resultText
();
QStringList
lines
=
text
.
split
(
'\n'
,
QString
::
SkipEmptyParts
);
for
(
QString
const
&
line
:
lines
)
{
// raw format: e.g. ":100644 100644 08bc10d... 18f0501... M src/MainWindow.cpp"
DiffRaw
item
;
int
colon
=
line
.
indexOf
(
':'
);
int
tab
=
line
.
indexOf
(
'\t'
);
if
(
colon
>=
0
&&
colon
<
tab
)
{
QStringList
header
=
line
.
mid
(
colon
+
1
,
tab
-
colon
-
1
).
split
(
' '
,
QString
::
SkipEmptyParts
);
// コロンとタブの間の文字列を空白で分割
if
(
header
.
size
()
>=
5
)
{
QStringList
files
=
line
.
mid
(
tab
+
1
).
split
(
'\t'
,
QString
::
SkipEmptyParts
);
// タブより後ろはファイルパス
if
(
files
.
size
()
>
0
)
{
for
(
QString
&
file
:
files
)
{
file
=
Git
::
trimPath
(
file
);
}
item
.
a
.
id
=
header
[
2
];
item
.
b
.
id
=
header
[
3
];
item
.
a
.
mode
=
header
[
0
];
item
.
b
.
mode
=
header
[
1
];
item
.
state
=
header
[
4
];
item
.
files
=
files
;
list
.
push_back
(
item
);
}
}
}
}
return
list
;
}
QString
Git
::
diff_to_file
(
QString
const
&
old_id
,
QString
const
&
path
)
{
#if 1
QString
cmd
=
"diff --full-index -a %1 -- %2"
;
cmd
=
cmd
.
arg
(
old_id
).
arg
(
path
);
git
(
cmd
);
return
resultText
();
#else
return
diff_
(
old_id
,
new_id
);
#endif
}
QString
Git
::
getCurrentBranchName
()
{
if
(
isValidWorkingCopy
())
{
git
(
"rev-parse --abbrev-ref HEAD"
);
QString
s
=
resultText
().
trimmed
();
if
(
!
s
.
isEmpty
()
&&
!
s
.
startsWith
(
"fatal:"
)
&&
s
.
indexOf
(
' '
)
<
0
)
{
return
s
;
}
}
return
QString
();
}
QStringList
Git
::
getUntrackedFiles
()
{
QStringList
files
;
Git
::
FileStatusList
stats
=
status
();
for
(
FileStatus
const
&
s
:
stats
)
{
if
(
s
.
code
()
==
FileStatusCode
::
Untracked
)
{
files
.
push_back
(
s
.
path1
());
}
}
return
files
;
}
void
Git
::
parseAheadBehind
(
QString
const
&
s
,
Branch
*
b
)
{
ushort
const
*
begin
=
s
.
utf16
();
ushort
const
*
end
=
begin
+
s
.
size
();
ushort
const
*
ptr
=
begin
;
auto
NCompare
=
[](
ushort
const
*
a
,
char
const
*
b
,
size_t
n
){
for
(
size_t
i
=
0
;
i
<
n
;
i
++
)
{
if
(
*
a
<
(
unsigned
char
)
*
b
)
return
false
;
if
(
*
a
>
(
unsigned
char
)
*
b
)
return
false
;
if
(
!*
a
)
break
;
a
++
;
b
++
;
}
return
true
;
};
auto
SkipSpace
=
[
&
](){
while
(
ptr
<
end
&&
QChar
(
*
ptr
).
isSpace
())
{
ptr
++
;
}
};
auto
GetNumber
=
[
&
](){
int
n
=
0
;
while
(
ptr
<
end
&&
QChar
(
*
ptr
).
isDigit
())
{
n
=
n
*
10
+
(
*
ptr
-
'0'
);
ptr
++
;
}
return
n
;
};
if
(
*
ptr
==
'['
)
{
ptr
++
;
while
(
*
ptr
)
{
if
(
*
ptr
==
','
||
QChar
(
*
ptr
).
isSpace
())
{
ptr
++
;
}
else
if
(
NCompare
(
ptr
,
"ahead "
,
6
))
{
ptr
+=
6
;
SkipSpace
();
b
->
ahead
=
GetNumber
();
}
else
if
(
NCompare
(
ptr
,
"behind "
,
7
))
{
ptr
+=
7
;
SkipSpace
();
b
->
behind
=
GetNumber
();
}
else
{
break
;
}
}
}
}
QList
<
Git
::
Branch
>
Git
::
branches_
()
{
QList
<
Branch
>
branches
;
git
(
QString
(
"branch -v -a --abbrev=%1"
).
arg
(
GIT_ID_LENGTH
));
QString
s
=
resultText
();
#if DEBUGLOG
qDebug
()
<<
s
;
#endif
QStringList
lines
=
misc
::
splitLines
(
s
);
for
(
QString
const
&
line
:
lines
)
{
if
(
line
.
size
()
>
2
)
{
QString
name
;
QString
text
=
line
.
mid
(
2
);
int
pos
=
0
;
if
(
text
.
startsWith
(
'('
))
{
pos
=
text
.
indexOf
(
')'
);
if
(
pos
>
1
)
{
name
=
text
.
mid
(
1
,
pos
-
1
);
pos
++
;
}
}
else
{
while
(
pos
<
text
.
size
()
&&
!
QChar
::
isSpace
(
text
.
utf16
()[
pos
]))
{
pos
++
;
}
name
=
text
.
mid
(
0
,
pos
);
}
if
(
!
name
.
isEmpty
())
{
while
(
pos
<
text
.
size
()
&&
QChar
::
isSpace
(
text
.
utf16
()[
pos
]))
{
pos
++
;
}
text
=
text
.
mid
(
pos
);
Branch
b
;
if
(
name
.
startsWith
(
"HEAD detached at "
))
{
b
.
flags
|=
Branch
::
HeadDetached
;
name
=
name
.
mid
(
17
);
}
if
(
name
.
startsWith
(
"origin/"
))
{
name
=
name
.
mid
(
7
);
}
b
.
name
=
name
;
if
(
text
.
startsWith
(
"-> "
))
{
b
.
id
=
">"
+
text
.
mid
(
3
);
}
else
{
int
i
=
text
.
indexOf
(
' '
);
if
(
i
==
GIT_ID_LENGTH
)
{
b
.
id
=
text
.
mid
(
0
,
GIT_ID_LENGTH
);
}
while
(
i
<
text
.
size
()
&&
QChar
::
isSpace
(
text
.
utf16
()[
i
]))
{
i
++
;
}
text
=
text
.
mid
(
i
);
parseAheadBehind
(
text
,
&
b
);
}
if
(
line
.
startsWith
(
'*'
))
{
b
.
flags
|=
Branch
::
Current
;
}
branches
.
push_back
(
b
);
}
}
}
for
(
int
i
=
0
;
i
<
branches
.
size
();
i
++
)
{
Branch
*
b
=
&
branches
[
i
];
if
(
b
->
id
.
startsWith
(
'>'
))
{
QString
name
=
b
->
id
.
mid
(
1
);
if
(
name
.
startsWith
(
"origin/"
))
{
name
=
name
.
mid
(
7
);
}
for
(
int
j
=
0
;
j
<
branches
.
size
();
j
++
)
{
if
(
j
!=
i
)
{
if
(
branches
[
j
].
name
==
name
)
{
branches
[
i
].
id
=
branches
[
j
].
id
;
break
;
}
}
}
}
}
return
branches
;
}
QList
<
Git
::
Branch
>
Git
::
branches
()
{
#if 1
return
branches_
();
#else
std
::
string
dir
=
workingRepositoryDir
().
toStdString
();
LibGit2
::
Repository
r
=
LibGit2
::
openRepository
(
dir
);
return
r
.
branches
();
#endif
}
Git
::
CommitItemList
Git
::
log_all
(
QString
const
&
id
,
int
maxcount
)
{
CommitItemList
items
;
QString
text
;
QString
cmd
=
"log --pretty=format:
\"
commit:%H#parent:%P#author:%an#mail:%ae#date:%ci##%s
\"
--all -%1 %2"
;
cmd
=
cmd
.
arg
(
maxcount
).
arg
(
id
);
git
(
cmd
);
if
(
getProcessExitCode
()
==
0
)
{
text
=
resultText
().
trimmed
();
QStringList
lines
=
misc
::
splitLines
(
text
);
for
(
QString
const
&
line
:
lines
)
{
int
i
=
line
.
indexOf
(
"##"
);
if
(
i
>
0
)
{
Git
::
CommitItem
item
;
item
.
message
=
line
.
mid
(
i
+
2
);
QStringList
atts
=
line
.
mid
(
0
,
i
).
split
(
'#'
);
for
(
QString
const
&
s
:
atts
)
{
int
j
=
s
.
indexOf
(
':'
);
if
(
j
>
0
)
{
QString
key
=
s
.
mid
(
0
,
j
);
QString
val
=
s
.
mid
(
j
+
1
);
if
(
key
==
"commit"
)
{
item
.
commit_id
=
val
;
}
else
if
(
key
==
"parent"
)
{
item
.
parent_ids
=
val
.
split
(
' '
,
QString
::
SkipEmptyParts
);
}
else
if
(
key
==
"author"
)
{
item
.
author
=
val
;
}
else
if
(
key
==
"mail"
)
{
item
.
mail
=
val
;
}
else
if
(
key
==
"date"
)
{
item
.
commit_date
=
QDateTime
::
fromString
(
val
,
Qt
::
ISODate
).
toLocalTime
();
}
else
if
(
key
==
"debug"
)
{
}
}
}
// if (item.commit_date < limit_time) {
// break;
// }
items
.
push_back
(
item
);
}
}
}
return
items
;
}
Git
::
CommitItemList
Git
::
log
(
int
maxcount
)
{
#if 1
return
log_all
(
QString
(),
maxcount
);
#else
std
::
string
dir
=
workingRepositoryDir
().
toStdString
();
LibGit2
::
Repository
r
=
LibGit2
::
openRepository
(
dir
);
return
r
.
log
(
maxcount
);
#endif
}
bool
Git
::
clone
(
QString
const
&
url
,
QString
const
&
path
,
callback_t
callback
,
void
*
cookie
)
{
QString
basedir
;
QString
subdir
;
if
(
path
.
endsWith
(
'/'
)
||
path
.
endsWith
(
'\\'
))
{
auto
GitBaseName
=
[](
QString
location
){
int
i
=
location
.
lastIndexOf
(
'/'
);
int
j
=
location
.
lastIndexOf
(
'\\'
);
if
(
i
<
j
)
i
=
j
;
i
=
i
<
0
?
0
:
(
i
+
1
);
j
=
location
.
size
();
if
(
location
.
endsWith
(
".git"
))
{
j
-=
4
;
}
if
(
i
<
j
)
{
location
=
location
.
mid
(
i
,
j
-
i
);
}
return
location
;
};
basedir
=
path
;
subdir
=
GitBaseName
(
url
);
}
else
{
QFileInfo
info
(
path
);
basedir
=
info
.
dir
().
path
();
subdir
=
info
.
fileName
();
}
pv
->
working_repo_dir
=
misc
::
normalizePathSeparator
(
basedir
/
subdir
);
bool
ok
=
false
;
QDir
cwd
=
QDir
::
current
();
if
(
QDir
::
setCurrent
(
basedir
))
{
QString
cmd
=
"clone --progress
\"
%1
\"
\"
%2
\"
"
;
cmd
=
cmd
.
arg
(
url
).
arg
(
subdir
);
ok
=
git
(
cmd
,
false
,
true
);
QDir
::
setCurrent
(
cwd
.
path
());
}
return
ok
;
}
QString
Git
::
encodeQuotedText
(
const
QString
&
str
)
{
std
::
vector
<
ushort
>
vec
;
ushort
const
*
begin
=
str
.
utf16
();
ushort
const
*
end
=
begin
+
str
.
size
();
ushort
const
*
ptr
=
begin
;
vec
.
push_back
(
'\"'
);
while
(
ptr
<
end
)
{
ushort
c
=
*
ptr
;
ptr
++
;
if
(
c
==
'\"'
)
{
// triple quotes
vec
.
push_back
(
c
);
vec
.
push_back
(
c
);
vec
.
push_back
(
c
);
}
else
{
vec
.
push_back
(
c
);
}
}
vec
.
push_back
(
'\"'
);
return
QString
::
fromUtf16
(
&
vec
[
0
],
vec
.
size
());
}
bool
Git
::
commit_
(
QString
const
&
msg
,
bool
amend
)
{
QString
cmd
=
"commit"
;
if
(
amend
)
{
cmd
+=
" --amend"
;
}
QString
text
=
msg
.
trimmed
();
if
(
text
.
isEmpty
())
{
text
=
"no message"
;
}
text
=
encodeQuotedText
(
text
);
cmd
+=
QString
(
" -m %1"
).
arg
(
text
);
return
git
(
cmd
);
}
bool
Git
::
commit
(
QString
const
&
text
)
{
#if 1
return
commit_
(
text
,
false
);
#else
LibGit2
::
Repository
r
=
LibGit2
::
openRepository
(
workingRepositoryDir
().
toStdString
());
return
r
.
commit
(
text
.
toStdString
());
#endif
}
bool
Git
::
commit_amend_m
(
const
QString
&
text
)
{
return
commit_
(
text
,
true
);
}
void
Git
::
push_
(
bool
tags
)
{
QString
cmd
=
"push"
;
if
(
tags
)
{
cmd
+=
" --tags"
;
}
git
(
cmd
);
}
void
Git
::
push
(
bool
tags
)
{
#if 1
push_
(
tags
);
#else
LibGit2
::
Repository
r
=
LibGit2
::
openRepository
(
workingRepositoryDir
().
toStdString
());
r
.
push_
();
#endif
}
Git
::
FileStatusList
Git
::
status_
()
{
FileStatusList
files
;
if
(
git
(
"status -s -u --porcelain"
))
{
QString
text
=
resultText
();
QStringList
lines
=
misc
::
splitLines
(
text
);
for
(
QString
const
&
line
:
lines
)
{
if
(
line
.
size
()
>
3
)
{
FileStatus
s
(
line
);
if
(
s
.
code
()
!=
FileStatusCode
::
Unknown
)
{
files
.
push_back
(
s
);
}
}
}
}
return
files
;
}
Git
::
FileStatusList
Git
::
status
()
{
#if 1
return
status_
();
#else
std
::
string
repodir
=
workingRepositoryDir
().
toStdString
();
return
LibGit2
::
status
(
repodir
);
#endif
}
QByteArray
Git
::
cat_file_
(
QString
const
&
id
)
{
qDebug
()
<<
"cat_file: "
<<
id
;
#if 1
git
(
"cat-file -p "
+
id
);
return
pv
->
result
;
#else
std
::
string
dir
=
workingRepositoryDir
().
toStdString
();
LibGit2
::
Repository
r
=
LibGit2
::
openRepository
(
dir
);
return
r
.
cat_file
(
id
.
toStdString
());
#endif
}
bool
Git
::
cat_file
(
QString
const
&
id
,
QByteArray
*
out
)
{
if
(
isValidID
(
id
))
{
*
out
=
cat_file_
(
id
);
return
true
;
}
return
false
;
}
void
Git
::
revertFile
(
const
QString
&
path
)
{
#if 1
git
(
"checkout -- "
+
path
);
#else
std
::
string
dir
=
workingRepositoryDir
().
toStdString
();
LibGit2
::
Repository
r
=
LibGit2
::
openRepository
(
dir
);
r
.
revert_a_file
(
path
.
toStdString
());
#endif
}
void
Git
::
revertAllFiles
()
{
git
(
"reset --hard HEAD"
);
}
void
Git
::
removeFile
(
const
QString
&
path
)
{
git
(
"rm "
+
path
);
}
void
Git
::
stage
(
QString
const
&
path
)
{
git
(
"add "
+
path
);
}
void
Git
::
stage
(
QStringList
const
&
paths
)
{
QString
cmd
=
"add"
;
for
(
QString
const
&
path
:
paths
)
{
cmd
+=
' '
;
cmd
+=
'\"'
;
cmd
+=
path
;
cmd
+=
'\"'
;
}
git
(
cmd
);
}
void
Git
::
unstage
(
QString
const
&
path
)
{
git
(
"reset HEAD "
+
path
);
}
void
Git
::
unstage
(
QStringList
const
&
paths
)
{
QString
cmd
=
"reset HEAD"
;
for
(
QString
const
&
path
:
paths
)
{
cmd
+=
"
\"
"
;
cmd
+=
path
;
cmd
+=
'\"'
;
}
git
(
cmd
);
}
void
Git
::
pull
()
{
git
(
"pull"
);
}
void
Git
::
fetch
()
{
git
(
"fetch"
);
}
QStringList
Git
::
make_branch_list_
()
{
QStringList
list
;
QStringList
l
=
misc
::
splitLines
(
resultText
());
for
(
QString
const
&
s
:
l
)
{
if
(
s
.
startsWith
(
"* "
))
list
.
push_back
(
s
.
mid
(
2
));
if
(
s
.
startsWith
(
" "
))
list
.
push_back
(
s
.
mid
(
2
));
}
return
list
;
}
void
Git
::
createBranch
(
QString
const
&
name
)
{
git
(
"branch "
+
name
);
}
void
Git
::
checkoutBranch
(
QString
const
&
name
)
{
git
(
"checkout "
+
name
);
}
void
Git
::
mergeBranch
(
QString
const
&
name
)
{
git
(
"merge "
+
name
);
}
QStringList
Git
::
getRemotes
()
{
QStringList
ret
;
git
(
"remote"
);
QStringList
lines
=
misc
::
splitLines
(
resultText
());
for
(
QString
const
&
line
:
lines
)
{
if
(
!
line
.
isEmpty
())
{
ret
.
push_back
(
line
);
}
}
return
ret
;
}
Git
::
User
Git
::
getUser
(
GetUser
purpose
)
{
User
user
;
bool
global
=
purpose
==
Git
::
GetUserGlobal
;
bool
local
=
purpose
==
Git
::
GetUserLocal
;
QString
arg1
;
if
(
global
)
arg1
=
"--global"
;
if
(
local
)
arg1
=
"--local"
;
bool
chdir
=
!
global
;
if
(
git
(
QString
(
"config %1 user.name"
).
arg
(
arg1
),
chdir
))
{
user
.
name
=
resultText
().
trimmed
();
}
if
(
git
(
QString
(
"config %1 user.email"
).
arg
(
arg1
),
chdir
))
{
user
.
email
=
resultText
().
trimmed
();
}
return
user
;
}
void
Git
::
setUser
(
const
User
&
user
,
bool
global
)
{
bool
chdir
=
!
global
;
git
(
QString
(
"config %1 user.name %2"
).
arg
(
global
?
"--global"
:
""
).
arg
(
encodeQuotedText
(
user
.
name
)),
chdir
);
git
(
QString
(
"config %1 user.email %2"
).
arg
(
global
?
"--global"
:
""
).
arg
(
encodeQuotedText
(
user
.
email
)),
chdir
);
}
void
Git
::
getRemoteURLs
(
QList
<
Remote
>
*
out
)
{
out
->
clear
();
git
(
"remote -v"
);
QStringList
lines
=
misc
::
splitLines
(
resultText
());
for
(
QString
const
&
line
:
lines
)
{
int
i
=
line
.
indexOf
(
'\t'
);
int
j
=
line
.
indexOf
(
" ("
);
if
(
i
>
0
&&
i
<
j
)
{
Remote
r
;
r
.
remote
=
line
.
mid
(
0
,
i
);
r
.
url
=
line
.
mid
(
i
+
1
,
j
-
i
-
1
);
r
.
purpose
=
line
.
mid
(
j
+
1
);
if
(
r
.
purpose
.
startsWith
(
'('
)
&&
r
.
purpose
.
endsWith
(
')'
))
{
r
.
purpose
=
r
.
purpose
.
mid
(
1
,
r
.
purpose
.
size
()
-
2
);
}
out
->
push_back
(
r
);
}
}
}
void
Git
::
setRemoteURL
(
QString
const
&
remote
,
QString
const
&
url
)
{
QString
cmd
=
"remote set-url %1 %2"
;
cmd
=
cmd
.
arg
(
encodeQuotedText
(
remote
)).
arg
(
encodeQuotedText
(
url
));
git
(
cmd
);
}
// Git::FileStatus
QString
Git
::
trimPath
(
QString
const
&
s
)
{
ushort
const
*
begin
=
s
.
utf16
();
ushort
const
*
end
=
begin
+
s
.
size
();
ushort
const
*
left
=
begin
;
ushort
const
*
right
=
end
;
while
(
left
<
right
&&
QChar
(
*
left
).
isSpace
())
left
++
;
while
(
left
<
right
&&
QChar
(
right
[
-1
]).
isSpace
())
right
--
;
if
(
left
+
1
<
right
&&
*
left
==
'\"'
&&
right
[
-1
]
==
'\"'
)
{
// if quoted ?
left
++
;
right
--
;
QByteArray
ba
;
ushort
const
*
ptr
=
left
;
while
(
ptr
<
right
)
{
ushort
c
=
*
ptr
;
ptr
++
;
if
(
c
==
'\\'
)
{
c
=
0
;
while
(
ptr
<
right
&&
QChar
(
*
ptr
).
isDigit
())
{
// decode \oct
c
=
c
*
8
+
(
*
ptr
-
'0'
);
ptr
++
;
}
}
ba
.
push_back
(
c
);
}
return
QString
::
fromUtf8
(
ba
);
}
if
(
left
==
begin
&&
right
==
end
)
return
s
;
return
QString
::
fromUtf16
(
left
,
right
-
left
);
}
void
Git
::
FileStatus
::
parse
(
const
QString
&
text
)
{
data
=
Data
();
if
(
text
.
size
()
>
3
)
{
ushort
const
*
p
=
text
.
utf16
();
if
(
p
[
2
]
==
' '
)
{
data
.
code_x
=
p
[
0
];
data
.
code_y
=
p
[
1
];
int
i
=
text
.
indexOf
(
" -> "
,
3
);
if
(
i
>
3
)
{
data
.
rawpath1
=
text
.
mid
(
3
,
i
-
3
);
data
.
rawpath2
=
text
.
mid
(
i
+
4
);
data
.
path1
=
trimPath
(
data
.
rawpath1
);
data
.
path2
=
trimPath
(
data
.
rawpath2
);
}
else
{
data
.
rawpath1
=
text
.
mid
(
3
);
data
.
path1
=
trimPath
(
data
.
rawpath1
);
data
.
path2
=
QString
();
}
data
.
code
=
parseFileStatusCode
(
data
.
code_x
,
data
.
code_y
);
}
}
}
// Diff
void
Git
::
Diff
::
makeForSingleFile
(
Git
::
Diff
*
diff
,
const
QString
&
id
,
const
QString
&
path
,
QString
const
&
mode
)
{
QString
zeros
(
GIT_ID_LENGTH
,
'0'
);
diff
->
diff
=
QString
(
"diff --git a/%1 b/%2"
).
arg
(
path
).
arg
(
path
);
diff
->
index
=
QString
(
"index %1..%2 %3"
).
arg
(
zeros
).
arg
(
id
).
arg
(
0
);
diff
->
blob
.
a_id
=
zeros
;
diff
->
blob
.
b_id
=
id
;
diff
->
path
=
path
;
diff
->
mode
=
mode
;
diff
->
type
=
Git
::
Diff
::
Type
::
Added
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, Nov 14, 11:52 PM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
139276
Default Alt Text
Git.cpp (21 KB)
Attached To
Mode
R77 Guitar
Attached
Detach File
Event Timeline